如果我为值类型实现接口(interface)并尝试将其转换为该接口(interface)类型的列表,为什么这会导致错误,而引用类型却转换得很好?

这是错误:



我必须明确地使用Cast<T>方法进行转换,为什么?
由于IEnumerable是整个集合中的只读枚举,因此对我来说,直接将其强制转换毫无意义。

下面是示例代码来演示此问题:

    public interface I{}

    public class T : I{}

    public struct V: I{}

    public void test()
    {
        var listT = new List<T>();
        var listV = new List<V>();

        var listIT = listT.ToList<I>();     //OK
        var listIV = listV.ToList<I>();     //FAILS to compile, why?

        var listIV2 = listV.Cast<I>().ToList(); //OK

    }

最佳答案

Variance (covariance or contravariance)不适用于值类型,仅适用于引用类型:



引用类型变量中包含的值是引用(例如,地址),数据地址具有相同的大小,并且以相同的方式进行解释,而无需更改其位模式。

相反,包含在值类型变量中的值不具有相同的大小或相同的语义。将它们用作引用类型需要装箱,而装箱则要求编译器发出特定于类型的指令。编译器发出任何可能类型的值类型的装箱指令是不实际或高效的(有时甚至不可能),因此完全不允许使用方差。

基本上,由于从变量到实际数据的额外的间接层(引用),方差是实际的。因为值类型缺少该间接层,所以它们缺乏方差能力。

将以上内容与LINQ操作的工作方式结合起来:
Cast操作上载/装箱所有元素(如您所指出的那样,通过非通用IEnumerable访问它们),然后验证序列中的所有元素都可以成功地强制转换/取消装箱成所提供的类型,然后准确地做到这一点。 ToList操作枚举序列,并从该枚举返回一个列表。

每个人都有自己的工作。如果(比如说)ToList兼顾了两者的工作,那么它将具有两者的性能开销,这对于大多数其他情况是不希望的。

10-07 13:57
查看更多