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.
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.
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.
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.
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