List<T> 的每个元素都装箱了吗?

A.B*_*.B. -1 .net c# linq boxing

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

object o1 = 5;
double n3 = (double)o1;
Run Code Online (Sandbox Code Playgroud)

Cast()在 a 上使用 Linq 的方法List<T>会引发类似的异常:

List<int> _numbers = new List<int>() { 1, 2, 3, 4 };
// this is also not working
IEnumerable<double> nums = _numbers.Cast<double>();
Run Code Online (Sandbox Code Playgroud)
  1. 这是否意味着 a 的每个元素都List<T>被装箱?
  2. 是否可以在List<>不创建列表副本的情况下转换每个元素?
    (请不要使用 Linq 的Select()方法发布任何答案,因为它正在创建集合的副本)

xan*_*tos 5

我们在这里只讨论值类型。引用类型遵循不同的规则。

  1. 这是否意味着 a 的每个元素都List<T>被装箱?

不。Java 会这样做,T在运行时擦除所有类型并将所有类型重定向到单个List<Object>实现(称为类型擦除)。在 .NET 中,对于值类型, every List<ValueType1>, List<ValueType2>,List<ValueType3>在运行时接收不同的实现(请参阅此处)。

  1. 是否可以在List<>不创建列表副本的情况下转换每个元素?

当您将值类型转换为其他内容时,您正在制作它的副本。经常在堆栈上,但副本存在。您不能简单地将值类型作为不同的值类型来访问。

例如

public static void M1(long n) {
    M2((int)n);
}

public static void M2(int n) {
}
Run Code Online (Sandbox Code Playgroud)

在 IL asm 中被翻译成

IL_0000: ldarg.0
IL_0001: conv.i4
IL_0002: call void C::M2(int32)
IL_0007: ret
Run Code Online (Sandbox Code Playgroud)

哪里conv.i4

如果转换成功,则将结果值压入堆栈。

请注意,即使取出的元素也会List<int>导致复制:您没有访问 INSIDE 中的元素List<int>

public void M1(List<int> a) {
    int v = a[0];
    v = 6; // this won't modify a[0]
}
Run Code Online (Sandbox Code Playgroud)

使用数组(以及更新的 C#),您可以直接访问数组的元素:

public void M1(int[] a) {
    ref int r = ref a[0];
    r = 6; // this will modify a[0]
}
Run Code Online (Sandbox Code Playgroud)

这里的区别是,索引器List<T>(当你做出一个被调用的事情var foo = list[5]是一种方法,而阵列的索引是(阵列在.NET虚拟机的最低水平所定义的IL指令,并且具有处理它们的特殊 OPList<T>构建在数组的“顶部”)。即使在SharpLab上也可以很容易地看到这一点