排序值类型会创建副本吗?

时间:2018-10-02 10:16:29

标签: c#

如何在C#中实现对值类型的排序?

我无法删除结构的(隐式)默认构造函数,以查看编译器/运行时未调用它,但我怀疑它在排序时会进行复制,因为即使持有一个临时(值)来进行交换也意味着复制,虽然可以用指针代替,但是在泛型代码中我看不到任何指示它对值类型和引用类型有特殊作用的东西。

IL

IL_0060: ldloc.0      // fooByValues
IL_0061: ldloc.1      // comparer
IL_0062: callvirt     instance void class [System.Collections]System.Collections.Generic.List`1<valuetype DevOpsCourse.Tests.Common.FunctionalComparerTest/FooByValue>::Sort(class [System.Runtime]System.Collections.Generic.IComparer`1<!0/*valuetype DevOpsCourse.Tests.Common.FunctionalComparerTest/FooByValue*/>)
IL_0067: nop          

仅指我无法查看的虚拟调用,因此我不知道它是如何实现的。

我也找不到与此有关的任何文档。

3 个答案:

答案 0 :(得分:2)

对于标题中提出的问题,答案是肯定的。


您在这里看不到任何特别之处。值类型的变量自身保存值 。每当您看到对值类型的变量的任何形式的赋值,都将是副本。

引用类型的变量将引用保存到保存数据的实际对象。当您对引用类型的变量执行赋值操作时,您会获得 reference 的副本,但引用仍引用同一对象。

这就是为什么您在这里看不到任何特别的地方-只是分配了对值类型和引用类型都“做正确的事”的变量。

(并且引用类型用作参数时,引用类型不会被引用传递。默认情况下,参数传递始终按值传递,但是传递的是一个变量,而不是一个 value < / em>)


  

尽管可以用指针代替

请记住,大多数结构无论如何都应该很小。交换指向它们的指针(即使存在这样的指针)也可能比交换值要耗费更多的工作,因为指针可能比结构大。

例如值类型的数组是包含实际值的内存块,而不是包含指向值的指针的内存块。交换两个这样的值的位置的唯一方法是从字面上覆盖它们。

答案 1 :(得分:1)

BCL中的排序算法会创建副本。它们本质上与以下内容相同:

struct MyStruct { ... }

MyStruct a = ...;
MyStruct b = a; //copy

这是一个简单的任务。

结构构造函数在这里不起作用。 .NET没有复制构造函数的概念。另外,此处未调用默认构造函数。总是通过简单地复制所有字段(基本上是memcpy)来复制结构。

即使分配是通过指针进行的,这仍然会做同样的事情。实际上,可以通过称为托管指针的IL概念来完成诸如array[0] = array[1];之类的数组访问。使用托管指针,您可以获得指向主题的指针并直接读取和写入该子对象。 C#现在通过其最新的ref功能公开了这一点。你可以说:

MyStruct[] array = ...;
ref MyStruct item0 = ref array[0];
item0 = ...; //updates the array

这是安全的托管代码。

答案 2 :(得分:1)

List<T>.Sort的实现委托给Array.Sort,它在交换操作期间确实创建了数组成员的临时副本。对于值类型,这意味着将复制需要移动的值。