C#中的原始类型容器采用值语义还是指针/引用语义?

3
当在C#中创建一个原始类型的List<>(例如,List<int>)时,列表内部的元素是按值存储,还是按引用存储?
换句话说,C#的List<int>是否等同于C++的std::vector<int>或C++的std::vector<shared_ptr<int>>?

@fontanini:我该如何“尝试自己”? - user1149224
3
要确定是否/在哪里进行了复制,您需要具有可变值类型,如果您不已经完全了解其工作方式,则测试将非常困难,因此,不,自己尝试并不容易。 - Servy
创建一个整数列表。创建一个整数。将其插入列表中。修改原始整数。打印存储在列表中的整数值。如果它们相等,则通过引用传递,否则通过值传递。 - mfontanini
虽然您提到了C++并提供了C++的伪代码,但这不是一个C++问题 - 所以我已经移除了该标签。 - John Dibling
3个回答

6

List<int> 内部会拥有一个 int[]。通常不需要装箱-值会直接存储在数组中。当然,如果您选择将 List<T> 用作非泛型 IList,其中 API 是基于 object 定义的,那么就会进行装箱操作:

List<int> list1 = new List<int>();

// No boxing or unboxing here
list1.Add(5);
int x = list1[0];

// Perfectly valid - but best avoided
IList list2 = new List<int>();

// Boxed by the caller, then unboxed internally in the implementation
list2.Add(5);

// Boxed in the implementation, then unboxed by the caller
int y = (int) list2[0];

请注意,“按引用存储”这一术语很容易引起混淆 - “按引用”一词通常在参数传递的上下文中使用,其含义有所不同。
例如,List<string> 包含一个数组,其中每个元素值都是引用,而在 List<int> 中,每个元素值只是一个 int。唯一涉及到的引用是调用者对 List<int> 的引用和对数组的内部引用。(即使元素类型是值类型,数组类型本身也始终是引用类型。)

0

值类型按值存储(例如原始类型和结构体),引用类型按引用存储(例如类)。


3
在我看来,“按引用传递”这个短语在这里并不太有用。它太像“通过引用传递”,但两者有些不同。对于引用类型,元素值仅仅是引用。 - Jon Skeet
所有的列表都存储值。对于类,该值恰好是一个引用,但它仍然按值存储。(就像所有参数都是按值传递一样,除非明确使用 out/ref。) - Servy
1
我不确定为什么在C#中选择了“引用(reference)”这个术语而不是指针(pointer),因为引用类型就是指针,按引用传递的意思也是如此(而将引用类型按引用传递则是指向指针的指针,或双重间接)。当你只考虑指针时,谈论引用类型和引用通常更容易。 - David Anderson
我知道C#有指针并且它们与引用(reference)不同,但是引用在内部使用指针进行实现,具有指针语义,并且在你以指针语义思考引用时更容易理解。那只是我在这里说的全部,但我可以说我没有学习引用的实际实现细节。Eric指出指针的目的是将变量本身作为数据来操作,而不是将变量的值作为数据来操作,这一点我还没有完全理解。 - David Anderson
1
@DavidAnderson:引用可能会被实现为指针。但它们不一定是。它们只是导航到对象的方式。例如,参见http://blog.juma.me.uk/2008/10/14/32-bit-or-64-bit-jvm-how-about-a-hybrid/ - 不幸的是链接无法使用,但它是关于JVM模式的,其中引用被“压缩”以允许许多引用作为32位值存储,即使在64位地址空间中也是如此。 - Jon Skeet
显示剩余2条评论

0

如果你写了这样的代码,会发生什么呢:

struct MutableValueType
{
  public int ChangableInt32;
}

static class Program
{
  static void Main()
  {
     var li = new List<MutableValueType>();
     li.Add(new MutableValueType());
     li[0].ChangableInt32 = 42;
  }
}

你会修改结构体的副本,还是会改变在 List<> 内部的副本?编译器会警告你吗?我想尝试一下。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接