.NET结构是值类型,这意味着如果函数A创建结构并调用函数B(它试图更改结构),B将获得结构的新副本,因此更改将不适用于A的结构
结构可能比CLR的其他值类型大得多。假设一个函数创建一个大型结构,调用另一个函数并将结构传递给函数。这种情况持续了10个级别,但所有函数只需要从结构中读取数据,并且不会更改任何字段。如果确实在每个函数调用中创建了结构的新副本,则上述方案将导致分配许多不需要的结构。
C语言用户可以通过传递指针而不是结构本身来避免这种情况,如果内部函数不应该更改结构中的数据,则可以使用const关键字。
但是,C#有引用而不是指针,并且没有“const ref”这样的东西。
我的问题是:.NET是否以这样的方式进行优化:只有在函数尝试更改内部字段时才知道复制结构,或者只有在将结构传递给另一个函数时才创建新副本?
答案 0 :(得分:4)
上述场景将导致分配许多不需要的结构
在“分配”方面考虑这一点与实际发生的情况非常不匹配。结构值通过CPU寄存器传递,如果它们变大或者有太多参数要传递,则会传递堆栈。这很便宜,没有调用运行时支持方法,也没有“释放”分配的概念。
这在本机C / C ++代码中的工作方式几乎相同,但需要注意的是x86 JIT编译器往往会生成更好的代码,因为struct传递总是在本机调用约定(除了__fastcall之外)中通过堆栈。也支持指针,就像在C中一样,您只需使用 ref 关键字声明参数。减去 const 关键字,你必须没有那个。
当struct大于16个字节时,传递结构的成本会急剧上升。它不再被推入堆栈,抖动生成代码以将指针传递给本地堆栈帧上的值的副本。实际上,该值在调用站点和被调用者处被复制两次。这就是.NET框架指南建议在结构变得太大时切换到类的原因。然而,通过ref传递也可以避免复制,但需要注意的是所有成员访问都是间接的。就像在C中一样。
不要犹豫使用类而不是struct,垃圾收集器非常有效。
答案 1 :(得分:1)
您可以通过引用传递值类型:
public void SomeMethod(ref SomeValueType someValue)
但是要回答你的具体问题,不,我不相信有任何这样的优化,我也不会觉得它们是可取的。我宁愿有一致的行为。如果你正在使用大量的价值类型并且无缘无故地传递它们,那你就错了。
答案 2 :(得分:0)
如果你的结构包含属性而不是字段,那么你可以声明一个结构的只读接口,并将引用传递给它。
public interface IMyReadOnlyStruct
{
int A { get; }
}
public struct MyStruct: IMyReadOnlyStruct
{
public int A { get; set; }
}
public void MyReadOnlyMethod(ref IMyReadOnlyStruct a)
{
....
}
这也是我将readonly引用传递给对象的方式。因为你可以做到这一点C#不需要支持const。使用接口可以使用const引用,也可以使用只能访问对象属性的任何给定子集的引用。
不幸的是,如果您的结构包含字段,那么您无法执行此操作。但是在那种情况下我会改变结构。
答案 3 :(得分:0)
回答这个问题;是否需要复制结构:写入字段/变量或传入方法。这没有被优化掉,也不需要。
如果它是一个结构,它应该是不可变的 - 那么改变字段的另一个方法的问题是没有意义的:它不应该改变它们,它们应该是只读。
重新过大的问题;如果它超大,它不应该是一个结构;它应该是一个类 - 但如果它代表一个谨慎的价值,你也可能希望即使在课堂形式中也保持不变。
这两个问题的另一个答案是将“ref”传递给方法,或者将封装为字段,而不是将属性封装在类上,但IMO中的任何一个通常意味着它们使用的是结构不正确。
我在这里要做的真正改变是评估这是否 是一个“值”。价值观永远不变;结构因此不应该变异。如果你不能说出你的类型,它可能不应该是一个结构。