如果我为值类型实现接口(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
兼顾了两者的工作,那么它将具有两者的性能开销,这对于大多数其他情况是不希望的。