Why is List<T> not valid on an covariant interface MyInterface<out T>

倾然丶 夕夏残阳落幕 提交于 2019-11-28 03:14:40

问题


Follow up question to a previous question, this has been identified as a co-variance issue. Taking this one step further, if I modify IFactory as follows:

class Program
{
    static void Main(string[] args)
    {
        IFactory<IProduct> factory = new Factory();
    }
}

class Factory : IFactory<Product>
{
}

class Product : IProduct
{
}

interface IFactory<out T> where T : IProduct
{
    List<T> MakeStuff();
}

interface IProduct
{
}

I get:

Invalid variance: The type parameter T must be invariantly valid on Sandbox.IFactory.MakeStuff(). T is covariant.

Why is this not invariantly valid? How can/should this be resolved?


回答1:


@Craig's answer is correct. To resolve, change it to:

IEnumerable<T> MakeStuff()

EDIT: As to the why, look at the definition of IEnumerable<T> Interface:

public interface IEnumerable<out T> : IEnumerable

Note that the IList<T> Interface does not have the out keyword. Variance is supported for generic type parameters in interfaces and delegates, not classes, so it doesn't apply to List<T>.




回答2:


The other answers are correct but it is instructive to reason out why the compiler flags this as unsafe. Suppose we allowed it; what could go wrong?

class Sprocket: Product {}
class Gadget : Product {}
class GadgetFactory : IFactory<Gadget> 
{ 
    public List<Gadget> MakeStuff() 
    { 
        return new List<Gadget>() { new Gadget(); } 
    }
}
... later ...
IFactory<Gadget> gf = new GadgetFactory();
IFactory<Product> pf = gf; // Covariant!
List<Product> pl = pf.MakeStuff(); // Actually a list of gadgets
pl.Add(new Sprocket());

and hey, we just added a sprocket to a list that can only contain gadgets.

There is only one place where the compiler can detect the problem, and that's in the declaration of the interface.

Sorry about the somewhat excessively jargonish error message. I couldn't come up with anything better.




回答3:


Because you can add and remove objects from a List<T>, T must always be invariant, even if the list is a function result. Having executed var l = MakeStuff() you can then put stuff in the list or take it out, so T must be invariant.



来源:https://stackoverflow.com/questions/6390728/why-is-listt-not-valid-on-an-covariant-interface-myinterfaceout-t

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!