问题
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