List<T> 的每个元素都装箱了吗?
Is each element of a List<T> boxed?
装箱的 T
只能拆箱到 T
。例如,这不起作用:
object o1 = 5;
double n3 = (double)o1;
在 List<T>
上使用 Linq 的 Cast()
方法会引发类似的异常:
List<int> _numbers = new List<int>() { 1, 2, 3, 4 };
// this is also not working
IEnumerable<double> nums = _numbers.Cast<double>();
- 这是否意味着
List<T>
的每个元素都被装箱了?
- 是否可以在不创建列表副本的情况下转换
List<>
的每个元素?
(请不要 post 使用 Linq 的 Select()
方法进行任何回答,因为它正在创建集合的副本)
我们这里只讨论值类型。引用类型遵循不同的规则。
- 这是否意味着
List<T>
的每个元素都被装箱了?
没有。 Java 做到了,在运行时擦除所有类型 T
并将所有类型重定向到单个 List<Object>
实现(称为 type erasure). In .NET, for value types, every List<ValueType1>
, List<ValueType2>
, List<ValueType3>
receives a distinct implementation at runtime (see here)。
- 是否可以在不创建列表副本的情况下转换
List<>
的每个元素?
当您将值类型转换为其他内容时,您正在复制它。通常在堆栈上,但副本存在。您不能简单地将值类型作为不同的值类型进行访问。
public static void M1(long n) {
M2((int)n);
}
public static void M2(int n) {
}
在 IL asm 中被翻译成
IL_0000: ldarg.0
IL_0001: conv.i4
IL_0002: call void C::M2(int32)
IL_0007: ret
其中 conv.i4
:
If the conversion is successful, the resulting value is pushed onto the stack.
请注意,即使 退出 List<int>
的一个元素也会导致创建一个副本:您没有访问 List<int>
中的元素:
public void M1(List<int> a) {
int v = a[0];
v = 6; // this won't modify a[0]
}
使用数组(以及较新的 C#),您可以直接访问数组的元素:
public void M1(int[] a) {
ref int r = ref a[0];
r = 6; // this will modify a[0]
}
这里的区别在于List<T>
的索引器(当你做一个var foo = list[5]
时调用的东西是一个方法,而索引器数组的是 IL 指令(数组定义在 .NET 虚拟机的最低级别,并且有特殊的 OP 来处理它们,List<T>
构建在数组的“顶部”)。即使这样也可以在 SharpLab.
上很容易看到
装箱的 T
只能拆箱到 T
。例如,这不起作用:
object o1 = 5;
double n3 = (double)o1;
在 List<T>
上使用 Linq 的 Cast()
方法会引发类似的异常:
List<int> _numbers = new List<int>() { 1, 2, 3, 4 };
// this is also not working
IEnumerable<double> nums = _numbers.Cast<double>();
- 这是否意味着
List<T>
的每个元素都被装箱了? - 是否可以在不创建列表副本的情况下转换
List<>
的每个元素?
(请不要 post 使用 Linq 的Select()
方法进行任何回答,因为它正在创建集合的副本)
我们这里只讨论值类型。引用类型遵循不同的规则。
- 这是否意味着
List<T>
的每个元素都被装箱了?
没有。 Java 做到了,在运行时擦除所有类型 T
并将所有类型重定向到单个 List<Object>
实现(称为 type erasure). In .NET, for value types, every List<ValueType1>
, List<ValueType2>
, List<ValueType3>
receives a distinct implementation at runtime (see here)。
- 是否可以在不创建列表副本的情况下转换
List<>
的每个元素?
当您将值类型转换为其他内容时,您正在复制它。通常在堆栈上,但副本存在。您不能简单地将值类型作为不同的值类型进行访问。
public static void M1(long n) {
M2((int)n);
}
public static void M2(int n) {
}
在 IL asm 中被翻译成
IL_0000: ldarg.0
IL_0001: conv.i4
IL_0002: call void C::M2(int32)
IL_0007: ret
其中 conv.i4
:
If the conversion is successful, the resulting value is pushed onto the stack.
请注意,即使 退出 List<int>
的一个元素也会导致创建一个副本:您没有访问 List<int>
中的元素:
public void M1(List<int> a) {
int v = a[0];
v = 6; // this won't modify a[0]
}
使用数组(以及较新的 C#),您可以直接访问数组的元素:
public void M1(int[] a) {
ref int r = ref a[0];
r = 6; // this will modify a[0]
}
这里的区别在于List<T>
的索引器(当你做一个var foo = list[5]
时调用的东西是一个方法,而索引器数组的是 IL 指令(数组定义在 .NET 虚拟机的最低级别,并且有特殊的 OP 来处理它们,List<T>
构建在数组的“顶部”)。即使这样也可以在 SharpLab.