C# 列表中的每个元素是否都已装箱?

C# 列表中的每个元素是否都已装箱?,c#,.net,linq,boxing,C#,.net,Linq,Boxing,装箱的T只能解装箱到T。例如,这不起作用: object o1 = 5; double n3 = (double)o1; 在列表上使用Linq的Cast方法会引发类似的异常: List<int> _numbers = new List<int>() { 1, 2, 3, 4 }; // this is also not working IEnumerable<double> nums = _numbers.Cast<double>(); 这是否

装箱的T只能解装箱到T。例如,这不起作用:

object o1 = 5;
double n3 = (double)o1;
在列表上使用Linq的Cast方法会引发类似的异常:

List<int> _numbers = new List<int>() { 1, 2, 3, 4 };
// this is also not working
IEnumerable<double> nums = _numbers.Cast<double>();
这是否意味着列表中的每个元素都已装箱? 是否可以在不创建列表副本的情况下强制转换列表的每个元素? 请不要使用Linq的Select方法发布任何答案,因为它正在创建集合的副本
这里我们只讨论值类型。引用类型遵循不同的规则

这是否意味着列表中的每个元素都已装箱? 不,Java会这样做,在运行时擦除所有类型T,并将所有类型重定向到它所调用的单个列表实现。在.NET中,对于值类型,每个列表、列表、列表在运行时都会收到一个不同的实现,请参见

是否可以在不创建列表副本的情况下强制转换列表的每个元素? 当您将一个值类型强制转换为其他类型时,您是在复制它。通常在堆栈上,但副本存在。不能简单地以不同的值类型访问值类型

:

在IL asm中翻译为

IL_0000: ldarg.0
IL_0001: conv.i4
IL_0002: call void C::M2(int32)
IL_0007: ret
其中:

如果转换成功,则将结果值推送到堆栈上

请注意,即使从列表中取出一个元素,也会导致复制:您没有访问列表中的元素:


这里的区别在于,当您创建一个var foo=List[5]时调用的List的索引器是一个方法,而数组的索引器是一个IL指令数组,它是在.NET虚拟机的最低级别上定义的,并且有特殊的操作来处理它们,列表是建立在数组之上的。即使这样,也很容易在上看到。

字符串永远不会被装箱,因为它已经是一个对象了。装箱仅适用于ValueType。是的,装箱需要创建一个新的装箱对象,因此不,您不能将数字列表强制转换为其他数字类型。列表中没有装箱。泛型有两个独立的运行时实现;一个用于引用,一个用于值。Enumerable.Cast专门用于从非泛型IEnumerable强制转换项,但是,当您执行此操作时,确实会对项进行装箱,因为这些项首先装箱,然后尝试将其作为double解除装箱,但这是Enumerable.Cast特有的。否-.Select不会创建集合的副本,它会创建一个迭代-就像.Cast一样,因此如果你不喜欢其中一个,你也不应该喜欢另一个。要添加到@JeroenMostert的注释中,Cast是IEnumerable的一个扩展方法,而IEnumerable.Current是一个对象,这意味着,当Cast方法从IEnumerable检索int时,每个int都将被装箱。如果你想从整数列表中创建一个双倍列表,你需要创建一个新的列表。如果您只需要一个IEnumerable,那么Select是作业的正确工具,而不是复制整个作业list@JeroenMostert如果我记得那是假的。。。所有引用类型都有一个单独的实现,每种值类型都有一个专门的实现。请参见@xanatos:yes,很遗憾,无法及时编辑我的注释。我应该说泛型有两种运行时实现,而不是两种。
IL_0000: ldarg.0
IL_0001: conv.i4
IL_0002: call void C::M2(int32)
IL_0007: ret
public void M1(List<int> a) {
    int v = a[0];
    v = 6; // this won't modify a[0]
}
public void M1(int[] a) {
    ref int r = ref a[0];
    r = 6; // this will modify a[0]
}