Assigning IEnumerable (Covariance)

∥☆過路亽.° 提交于 2019-12-07 01:29:55

问题


Since IEnumerable has a covariant parameter in C# 4.0 I am confused how it is behaving in the following code.

public class Test
{
    IEnumerable<IFoo> foos;

    public void DoTestOne<H>(IEnumerable<H> bars) where H : IFoo
    {
        foos = bars;
    }

    public void DoTestTwo(IEnumerable<IBar> bars)
    {
        foos = bars;
    }
}
public interface IFoo
{
}
public interface IBar : IFoo
{
}

So basically the DoTestOne method doesn't compile while DoTestTwo does. In addition to why it doesn't work, if anyone knows how I can achieve the effect of DoTestOne (assigning an IEnumberable<H> where H : IFoo to an IEnumberable<IFoo>) I would appreciate the help.


回答1:


If you know that H will be a class, this does work:

    public void DoTestOne<H>(IEnumerable<H> bars) where H : class, IFoo
    {
        foos = bars;
    }

The issue here is that if H is a value type, the covariance is not exactly what you'd expect, as IEnumerable<MyStruct> actually returns the value types whereas IEnumerable<IFoo> has to return boxed instances. You can use an explicit Cast<IFoo> to get around this, if necessary.




回答2:


You simply need a cast to IEnumerable<IFoo> in there:

public void DoTestOne<H>(IEnumerable<H> bars) where H : IFoo
{
    foos = (IEnumerable<IFoo>)bars;
}

Edit courtesy of Dan Bryant: using foos = bars.Cast<IFoo>() instead of above circumvents the InvalidCastException when H is a struct.




回答3:


You forgot a cast in your return or a "class" identifier in your generic constraint. What your doing is of course possible, reference below.

From: http://msdn.microsoft.com/en-us/library/d5x73970.aspx

where T : <interface name>

The type argument must be or implement the specified interface.
Multiple interface constraints can be specified. The constraining interface can also be generic.



回答4:


Within the .net Runtime, every value type has an associated heap object type with the same name. In some contexts, the value type will be used; in other contexts, the heap type. When a storage location (variable, parameter, return value, field, or array slot) of value type is declared, that storage location will hold the actual contents of that type. When a storage location of class type is declared, it will hold either null or a reference to a heap object that is stored elsewhere. Interface-type storage locations are treated like reference-type ones, and hold heap references even if some (or all) of the implementations of the interface are actually value types.

An attempt to store a value type into a reference-type storage location will cause the system to create a new instance of the heap type associated with the value type, copy all the fields from the original storage location to corresponding fields in the new instance, and store a reference to that instance, a process called "boxing". An attempt to cast a heap reference to a value-type storage location will check whether it refers to an instance of the heap type associated with the value type; if it does, the fields of the heap object will be copied ("unboxed") into the corresponding ones in the value-type storage location.

Although it may look as though a type like System.Int32 derives from System.Object, that's only half true. There is a heap object type System.Int32, which does indeed derive from System.Object, but a variable of type System.Int32 doesn't hold a reference to such an object. Instead, such a variable holds the actual data associated with that integer; the data itself is just a collection of bits, and doesn't derive from anything.

If one thinks of interface-type storage locations as holding "Something derived from System.Object which implements interface _", then instances of any class type which implements that interface are an instance of that type, but instances of a value types--even if they are convertible to other types--are not instances of any other types. Code which uses an IEnumerator<IFoo> doesn't just want its Current method to return something which could be convertible to an IFoo, or implements IFoo; it wants it to return something that is a derivative of Object that implements IFoo. Consequently, for an IEnumerable<T> to substitute for an IEnumerable<IFoo>, it's necessary that T be constrained both to implement IFoo and to be a proper derivative of System.Object.



来源:https://stackoverflow.com/questions/12962388/assigning-ienumerable-covariance

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