C# 默认情况下,通过引用传递不可变的值类型
通常,我在结构和类之间进行选择,不是因为内存问题,而是因为类型的语义。我的一些值类型具有相当大的内存占用,有时太大而无法一直复制此数据。所以我想知道总是通过引用传递不可变的值对象是否是个好主意?由于对象是不可变的,它们不能被通过引用接受它们的方法修改。通过引用传递时是否存在其他问题 所以我想知道始终通过引用传递不可变值对象是否是一个好主意?由于对象是不可变的,它们不能被通过引用接受它们的方法修改。通过引用传递时是否存在其他问题 你到底是什么意思还不清楚。假设您的意思是将其作为C# 默认情况下,通过引用传递不可变的值类型,c#,value-type,C#,Value Type,通常,我在结构和类之间进行选择,不是因为内存问题,而是因为类型的语义。我的一些值类型具有相当大的内存占用,有时太大而无法一直复制此数据。所以我想知道总是通过引用传递不可变的值对象是否是个好主意?由于对象是不可变的,它们不能被通过引用接受它们的方法修改。通过引用传递时是否存在其他问题 所以我想知道始终通过引用传递不可变值对象是否是一个好主意?由于对象是不可变的,它们不能被通过引用接受它们的方法修改。通过引用传递时是否存在其他问题 你到底是什么意思还不清楚。假设您的意思是将其作为ref或out参数传
ref
或out
参数传递,那么该方法只能将一个新实例分配给存储位置。这将修改调用者看到的内容,因为被调用者中的存储位置是调用者传递的存储位置的别名
如果您处理的是内存问题,因为复制了
- 它在逻辑上表示单个值,类似于基本类型(整数、双精度等)
- 它的实例大小小于16字节
- 它是不变的
- 它不必经常装箱
ref
是安全的:
// int is immutable, right?
int x = 5;
Foo(ref x);
Console.WriteLine(x); // Eek, prints 6...
...
void Foo(ref int y)
{
y = 6;
}
我们没有改变值的一部分-我们用一个完全不同的值替换x
的整个值
当涉及到引用类型时,不变性更容易考虑-尽管即使这样,您也可以拥有一个本身不会更改的对象,但可以引用可变对象…我认为实际上,当您所做的一切都指向使用类时,使用结构是个坏主意
相关答案:乔恩的答案当然是正确的;我想补充一点:当您对值类型调用方法时,值类型已经通过引用传递。例如:
struct S
{
int x;
public S(int x) { this.x = x; }
public void M() { Console.WriteLine(this.x); }
}
方法M()在逻辑上与:
public static void M(ref S _this) { Console.WriteLine(_this.x); }
无论何时调用结构上的实例方法,我们都会向作为调用接收者的变量传递一个ref
那么,如果接收器不是一个变量呢?然后将该值复制到用作接收器的临时变量中。如果价值很大,这可能是一个昂贵的副本
值类型按值复制;这就是为什么它们被称为值类型。除非您打算非常小心地查找所有可能的昂贵副本并消除它们,否则我将遵循框架设计指南的建议:将结构保持在16字节以下,并按值传递它们
我还要强调Jon是对的:通过ref传递结构意味着传递对变量的引用,变量可以改变。这就是为什么它们被称为“变量”。C语言中没有C++的“const REF”;即使值类型本身看起来是“不可变的”,但这并不意味着持有它的变量是不可变的。在这个精心设计但富有教育意义的例子中,你可以看到一个极端的例子:
struct S
{
readonly int x;
public S(int x) { this.x = x; }
public void M(ref S s)
{
Console.WriteLine(this.x);
s = new S(this.x + 1);
Console.WriteLine(this.x);
}
}
M能写出两个不同的数字吗?您可能天真地认为结构是不可变的,因此x不能更改。但s和s都是变量,变量可以改变:
因为这个
和s
都是对q
的引用,没有任何东西阻止q
的改变;它不是只读的
简言之:如果我有很多数据想要传递,并且有很强的不可变保证,那么我会使用类,而不是结构。只有在这种情况下,如果您有一个已证明的性能问题,而该问题实际上是通过将其变为结构来解决的,请记住,复制大型结构可能非常昂贵。也许您需要重新考虑在结构和类之间做出决定的理由。“它的实例大小小于16字节”。你真的写了一个
类Vector3d
,因为它有24个字节吗?@hansmad这只是一个指南。每一个案例都不能被它覆盖,但它们是在做出决定时要考虑的事情/规则。@汉斯马德:如果类型很可能是通过价值传递的,那么,是的,你应该。如果值类型不太可能通过值传递——如果您只是创建一个值并在本地使用它,而不将它传递给另一个方法——那么,如果您愿意,就继续把它变大。@Hansmad:Rico Mariani对违反这些准则来创建Point3d
struct不可变结构可以被证明是可变的,即使它们的代码中没有任何东西在这种变异中合作。例如,即使可以自动写入KeyValuePair
的存储位置(因为它正好需要32位),但在这样的存储位置上有一个线程ToString()
,而另一个线程更新那里的值可能会导致ToString
返回旧的Key.ToString()
与新的值.ToString()连接起来。
。
S q = new S(1);
q.M(ref q);