1

Given these interfaces and classes

public interface IFoo { }

public interface IFooWrapper<I> where I : IFoo { }

public class Foo : IFoo { }
public class FooWrapper : IFooWrapper<Foo> { }

Why does this fail?

IFooWrapper<IFoo> foo = new FooWrapper();

I know I could use dynamic, here, but the real question is : I have a method that would receive an implementation of these interfaces, and it fails for the same reason :

Severity Code Description Project File Line Suppression State Error CS0266 Cannot implicitly convert type 'FooWrapper' to 'IFooWrapper'. An explicit conversion exists (are you missing a cast?)

The method signature would look like

void Register(IFooWrapper<IFoo> foo)
{
}

The compiler fails at the line

Register(new FooWrapper());
Yanick Rochon
  • 51,409
  • 25
  • 133
  • 214
  • Does this answer your question? [C# generic inheritance and covariance part 2](https://stackoverflow.com/questions/14263964/c-sharp-generic-inheritance-and-covariance-part-2) (and [About the lack of true generic polymorphism and the missing diamond operator in C#](https://stackoverflow.com/questions/58570948/how-to-create-list-of-open-generic-type-of-classt/58571001#58571001)) –  Jun 23 '21 at 21:45

1 Answers1

8

That's because of co- and contra-variance.

When something is a IFooWrapper<Foo>, that doesn't mean it's could be compile time converted to IFooWrapper<IFoo>. I think it's better explained here: Covariance and Contravariance (C#) | Microsoft Docs and still confused about covariance and contravariance & in/out.

Please note that you could mark interfaces in C# as co- or contra-variant. For example, if you have the below interface (notice the out I - it's now covariant for type parameter I), you could use IFooWrapper<IFoo> foo = new FooWrapper();

public interface IFooWrapper<out I> where I : IFoo { }

See demo in Fiddle

Instead of changing the interface, you could fix the method signature with generics:

void Register<T>(IFooWrapper<T> foo) where T: IFoo
{
}

That one could be called with IFooWrapper<Foo> and IFooWrapper<IFoo> - so this works: Register(new FooWrapper());

See also Fiddle

Julian
  • 33,915
  • 22
  • 119
  • 174