c#4.0: int a real subtype of object? covariance, ienumerable and value types

て烟熏妆下的殇ゞ 提交于 2019-12-18 15:35:11

问题


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 of object

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

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