c#4.0:int 对象的真正子类型?协方差、可枚举和值类型

发布于 2024-10-26 18:08:35 字数 868 浏览 9 评论 0原文

我想知道为什么 IEnumerable 不能分配给 IEnumerable。毕竟 IEnumerable 是少数支持协变的接口之一...

  • 子类型关系和协变的东西与引用类型
  • int 一起工作似乎是 的正确子类型object

然而,这两个功能的组合不起作用...

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

感谢您的帮助! 塞巴斯蒂安

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

溺渁∝ 2024-11-02 18:08:35

在装箱之前,值类型不是对象的 LSP 子类型。

方差不适用于值类型。完全没有。


证明 int 不是 object正确子类型(LSP 意义上的子类型):

有效:

object x = new object();
lock (x) { ... }

无效(违反可替代性):

int y = new int();
lock (y) { ... }

返回true:

object x = new object();
object a = x;
object b = x;
return ReferenceEquals(a, b);

返回 false(违反可替代性):

int y = new int();
object a = y;
object b = y;
return ReferenceEquals(a, b);

当然,问题的主题(界面差异)是第三个演示。

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.

乄_柒ぐ汐 2024-11-02 18:08:35

问题在于 object 是引用类型,而不是值类型。可以将 int 分配给 object 类型的变量的唯一原因是装箱。

为了将 List 分配给 IEnumerable,您必须对列表的每个元素进行装箱。您不能仅通过将引用分配给列表并将其称为不同的类型来做到这一点。

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.

一绘本一梦想 2024-11-02 18:08:35

简单化的答案是,这只是 C# 和 CLR 中实现方差的方式中的怪癖之一。

来自“泛型中的协变和逆变”

差异仅适用于参考
类型;如果您指定值类型
变体类型参数,该类型
参数是不变的
生成的构造类型。

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.

莫言歌 2024-11-02 18:08:35

.net 中的每个值类型都有一个相应的(“装箱的”)对象类型。非装箱值类型实际上位于对象类型层次结构之外,但编译器将执行从值类型到装箱类类型的扩展。有一个“类” Boxed会很有帮助。它将支持与 T 之间的扩展转换,但它将是一个类类型。在内部,我认为这就是编译器隐式执行的操作,但我不知道有什么方法可以显式执行此操作。对于像“整数”这样的任何特定类型,定义一个表现得像 Boxed的类是没有困难的。应该,但我不知道以通用方式做这样的事情有什么方法。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文