C# 默认情况下,通过引用传递不可变的值类型

C# 默认情况下,通过引用传递不可变的值类型,c#,value-type,C#,Value Type,通常,我在结构和类之间进行选择,不是因为内存问题,而是因为类型的语义。我的一些值类型具有相当大的内存占用,有时太大而无法一直复制此数据。所以我想知道总是通过引用传递不可变的值对象是否是个好主意?由于对象是不可变的,它们不能被通过引用接受它们的方法修改。通过引用传递时是否存在其他问题 所以我想知道始终通过引用传递不可变值对象是否是一个好主意?由于对象是不可变的,它们不能被通过引用接受它们的方法修改。通过引用传递时是否存在其他问题 你到底是什么意思还不清楚。假设您的意思是将其作为ref或out参数传

通常,我在结构和类之间进行选择,不是因为内存问题,而是因为类型的语义。我的一些值类型具有相当大的内存占用,有时太大而无法一直复制此数据。所以我想知道总是通过引用传递不可变的值对象是否是个好主意?由于对象是不可变的,它们不能被通过引用接受它们的方法修改。通过引用传递时是否存在其他问题

所以我想知道始终通过引用传递不可变值对象是否是一个好主意?由于对象是不可变的,它们不能被通过引用接受它们的方法修改。通过引用传递时是否存在其他问题

你到底是什么意思还不清楚。假设您的意思是将其作为
ref
out
参数传递,那么该方法只能将一个新实例分配给存储位置。这将修改调用者看到的内容,因为被调用者中的存储位置是调用者传递的存储位置的别名

如果您处理的是内存问题,因为复制了Stult的实例,您应该考虑做一个不可变的引用类型,很像字符串 我的一些值类型占用了相当大的内存

这表明,从实现的角度来看,它们不应该是值类型。来自“开发类库的设计指南”,章节:

除非类型具有以下所有特征,否则不要定义结构:

  • 它在逻辑上表示单个值,类似于基本类型(整数、双精度等)

  • 它的实例大小小于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);