简体   繁体   中英

How to cast a generic to the interface it implements when the type parameters on both implement a common interface in C#

Consider the following code:

public class Thing : IThing { }

public interface IThing {}

public interface IContainer<out T> where T : IThing { }

// This works
// public class Container<T> : IContainer<T> where T : IThing { }

// This doesn't work
public class Container<T> : IContainer<IThing> where T : IThing {}

internal class Program
{
  private static void Main(string[] args)
  {
    var concreteContainer = new Container<Thing>();
    var abstractContainer = (IContainer<Thing>) concreteContainer;
  }
}

On this line:

var abstractContainer = (IContainer<Thing>) concreteContainer;

You get the following runtime error: InvalidCastException: Unable to cast object of type 'CastTest.Container`1[CastTest.Thing]' to type CastTest.IContainer`1[CastTest.Thing]'.

Also if you have Resharper, it complains with, Suspecious cast: there is no type in the solution which is inherited from both 'Container<Thing>' and 'IContainer<Thing>' .

Why does there need to be a type that inherits from both? Doesn't Container<T> implement IContainer<IThing> ? Since Thing implements IThing , and T in Container<T> is guaranteed to implement IThing , it seems like I should be able to do this cast.

Doesn't Container<T> implement IContainer<IThing> ?

It does.

Since Thing implements IThing , and T in Container<T> is guaranteed to implement IThing , it seems like I should be able to do this cast.

out works the other way around. out means that if the type implements IContainer<Thing> , it automatically implements IContainer<IThing> as well. Not vice versa.

It's called out because it can return something. You might have for instance

interface IThing<out T> {
    T Prop { get; }
}

Now, IContainer<Apple> would automatically implement IContainer<Fruit> , and IContainer<Banana> would also automatically implement IContainer<Fruit> . That works, because something which returns an Apple can be interpreted as returning a Fruit . But if you only know it returns a Fruit , you don't know whether that Fruit is an Apple .

in works the way you ask. You might have for instance

interface IThing<in T> {
    void Act(T t);
}

Now, IContainer<Apple> does not automatically implement IContainer<Fruit> . That's because something which requires an Apple won't be able to accept arbitrary Fruit s. But something which only requires a Fruit does accept all Apple s.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM