问题
I wonder why IEnumerable<int>
can't be assigned to a IEnumerable<object>
. After all IEnumerable
is one of the few interfaces that supports covariance...
- The subtype relation and covariance stuff works with reference types
int
seems to be a proper subtype ofobject
The combination of both features doesn't work however...
class A
{
}
class B : A
{
}
class Program
{
static void Main(string[] args)
{
bool b;
b = typeof(IEnumerable<A>).IsAssignableFrom(typeof(List<B>));
Console.WriteLine("ienumerable of ref types is covariant: " + b); //true
b = typeof(IEnumerable<object>).IsAssignableFrom(typeof(List<int>));
Console.WriteLine("ienumerable of value tpyes is covariant: " + b); //false
b = typeof(object).IsAssignableFrom(typeof(int));
Console.WriteLine("int is a subtype of object: " + b); //true
}
}
thanks for your help! sebastian
回答1:
Value types aren't LSP-subtypes of object until they're boxed.
Variance doesn't work with value types. At all.
Demonstration that int
is not a proper subtype (subtype in the LSP sense) of object
:
Works:
object x = new object();
lock (x) { ... }
Does not work (substitutability violated):
int y = new int();
lock (y) { ... }
Returns true:
object x = new object();
object a = x;
object b = x;
return ReferenceEquals(a, b);
Returns false (substitutability violated):
int y = new int();
object a = y;
object b = y;
return ReferenceEquals(a, b);
Of course, the topic of the question (interface variance) is a third demonstration.
回答2:
The problem is that object
is a reference type, not a value type. The only reason you can assign an int
to a variable of type object
is boxing.
In order to assign List<int>
to IEnumerable<object>
you'd have to box each element of the list. You can't do that just by assigning the reference to the list and calling it a different type.
回答3:
The simplistic answer is that this is just one of the quirks in the way that variance is implemented in C# and the CLR.
From "Covariance and Contravariance in Generics":
Variance applies only to reference types; if you specify a value type for a variant type parameter, that type parameter is invariant for the resulting constructed type.
回答4:
Every value type in .net has a corresponding ("boxed") object type. Non-boxed value types are effectively outside the object type hierarchy, but the compiler will perform a widening from the value type to the boxed class type. It would be helpful to have a "class" Boxed<T> which would support a widening conversions to and from T, but which would be a class type. Internally, I think that's what the compiler's doing implicitly, but I don't know any way to do it explicitly. For any particular type like "integer", there would be no difficulty defining a class which would behave as a Boxed<Integer> should, but I don't know any way of doing such a thing in generic fashion.
来源:https://stackoverflow.com/questions/5413830/c4-0-int-a-real-subtype-of-object-covariance-ienumerable-and-value-types