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

时间:2012-04-10 14:43:45

标签: c# value-type

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

4 个答案:

答案 0 :(得分:14)

  

我的一些值类型具有相当大的内存占用

从实现的角度来看,这表明它们不应该是值类型。从“开发类库的设计指南”,第"Choosing Between Classes And Structures"部分:

  

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

     
      
  • 它逻辑上表示单个值,类似于基本类型(整数,双精度等)。
  •   
  • 实例大小小于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的整个值替换为完全不同的值。

当涉及引用类型时,不变性更容易思考 - 尽管如此,你可以拥有一个本身不会改变的对象,但可以引用可变对象... < / p>

答案 1 :(得分:11)

Jon的回答当然是对的;我会添加它:当您在值类型上调用方法时,值类型已经通过引用传递。例如:

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是对的:通过引用传递结构意味着将引用传递给变量,变量可以更改。这就是为什么他们被称为“变量”。 C#中没有“const ref”,就像C ++中的那样;即使值类型本身似乎是“不可变的”,也不意味着持有它的变量是不可变的。你可以在这个人为但教育的例子中看到一个极端的例子:

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 q = new S(1);
q.M(ref q);

打印1,2,因为thiss都是对q的引用,并且没有任何内容阻止q更改; 它不是只读

简而言之:如果我有很多数据需要传递并且有很强的保证它是不可变的,那么我将使用类,而不是结构。如果你有一个已经证明的性能问题,那么只在这个场景中使用一个结构,它实际上是通过使它成为一个结构来解决的,记住大型结构的复制可能非常昂贵。

答案 2 :(得分:3)

  

所以我想知道总是通过引用传递不可变值对象是一个好主意吗?由于对象是不可变的,因此不能通过引用接受它们的方法进行修改。通过引用传递时是否还有其他问题?

目前尚不清楚你的意思。假设您的意思是将其作为refout参数传递,则该方法只能将新实例分配给存储位置。这将修改调用者看到的内容,因为被调用者中的存储位置是调用者传递的存储位置的别名。

如果由于复制struct的实例而处理内存问题,则应考虑使用不可变的引用类型,就像string一样。

答案 3 :(得分:0)

我认为实际上糟糕的想法是当你所做的一切都指向使用类

时使用结构

相关答案:https://stackoverflow.com/questions/2440029/is-it-a-bad-practice-to-pass-structs-by-reference