C# 排序值类型是否创建副本?

C# 排序值类型是否创建副本?,c#,C#,如何在C#中实现值类型的排序 我无法删除structs的(隐式)默认构造函数以查看编译器/运行时是否调用它,但我怀疑它在排序时会进行复制,因为即使持有临时(值)进行交换也意味着复制,尽管这可以通过指针实现,但是我在泛型代码中看不到任何东西表明它对值类型和引用类型做了任何特殊的处理 IL IL_0060: ldloc.0 // fooByValues IL_0061: ldloc.1 // comparer IL_0062: callvirt instance void

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

我无法删除structs的(隐式)默认构造函数以查看编译器/运行时是否调用它,但我怀疑它在排序时会进行复制,因为即使持有临时(值)进行交换也意味着复制,尽管这可以通过指针实现,但是我在泛型代码中看不到任何东西表明它对值类型和引用类型做了任何特殊的处理

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          
IL_0060:ldloc.0//fooByValues
IL_0061:ldloc.1//比较器
IL_0062:callvirt实例无效类[System.Collections]System.Collections.Generic.List`1::Sort(类[System.Runtime]System.Collections.Generic.IComparer`1)
IL_0067:没有
只是指一个我无法查看的虚拟调用,所以我不知道它是如何真正实现的


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

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


你在这里看不到什么特别的东西。值类型的变量将值保存在其自身中。任何时候你看到任何形式的赋值给一个值类型的变量,那都是一个副本

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

这就是为什么在这里你看不到任何特别的东西——所有这些都只是赋值给变量,对于值类型和引用类型都“做正确的事情”

(当用作参数时,引用类型不按引用传递。默认情况下,参数传递始终按值传递,但传递的是变量,而不是值)


虽然这可以通过指针来实现

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


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

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


你在这里看不到什么特别的东西。值类型的变量将值保存在其自身中。任何时候你看到任何形式的赋值给一个值类型的变量,那都是一个副本

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

这就是为什么在这里你看不到任何特别的东西——所有这些都只是赋值给变量,对于值类型和引用类型都“做正确的事情”

(当用作参数时,引用类型不按引用传递。默认情况下,参数传递始终按值传递,但传递的是变量,而不是值)


虽然这可以通过指针来实现

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


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

BCL中的排序算法确实会创建副本。它们基本上做了与中相同的事情:

struct MyStruct { ... }

MyStruct a = ...;
MyStruct b = a; //copy
这是一个简单的任务

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

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

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

这是安全的托管代码。

BCL中的排序算法会创建副本。它们基本上做了与中相同的事情:

struct MyStruct { ... }

MyStruct a = ...;
MyStruct b = a; //copy
这是一个简单的任务

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

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

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

这是安全的托管代码。

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

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

Structs作为参数传递时被复制,引用对象是通过引用传递的。@Glubus我知道这一点。您可以使用内存探查器并观察正在创建的对象的数量。Wh