通过引用传递只是一种遗产吗?

Is pass by reference just a legacy?

也许这对我来说有点天真,但我真的无法 find/think 找到 "pass by reference" 的合适用例。更改不可变字符串(正如其他 Q/As 提到的那样)通常是可以避免的,返回多个变量通常通过返回元组、列表、数组等更好地处理。

example on MSDN 在我看来很糟糕;我只是在 Square 方法中返回一个值,而不是将其声明为 void.

在我看来,它有点像 C# 的遗留部分,而不是它的组成部分。比我聪明的人可以尝试解释为什么它仍然存在 and/or 一些实际实用的真实用例(即几乎在所有情况下都可以避免更改不可变字符串)。

假设你想要一个改变值的函数(注意,value,而不是 object),你也想要那个return 一些成功指标的功能。一个好的做法是 return 一个指示成功/失败的布尔值,但是值呢?所以你使用 ref:

bool Mutate(ref int val)
{
  if(val > 0)
  {
     val = val * 2;
     return true;
  }
  return false;
}

的确,在 C# 中通常有 refout 的替代方法 - 例如,如果您想要 return 多个值给调用者,您可以 return一个元组,或者一个自定义类型,或者接收一个引用类型作为参数并改变其中的多个值。

但是,在互操作等情况下,这些关键字仍然是一个方便的解决方案:

// C
int DoSomething(int input, int *output);

// C#
[DllImport(...)]
static extern int DoSomething(int input, ref int output);

P.S.: I followed up on some of the comments by and . I see now that they were right and I was wrong: my answer was somewhat misleading. The excellent article "Java is pass-by-value, dammit!" by Scott Stanchfield (Java-specific, but still mostly relevant to C#) finally convinced me so.

I'll leave the misleading bits of my answer striked through for now, but might later remove them.


通过引用传递不仅与 refout 参数一起使用。更重要的是,所有引用类型都通过引用传递(因此它们的名称),尽管这是透明发生的。

这里有三个频繁的传递引用用例:

  • 在传递大 struct 时防止复制它们。想象一下,您有一个 byte[] 数组,表示一个二进制大对象 (BLOB),大小可能为几兆字节 某些 struct 包含很多字段的类型。该类型的值可能会占用大量内存。现在您想将此值传递给某个方法。您真的要按值传递它,即创建一个临时副本吗?

    您可以通过 reference 传递它们来避免不必要的大型结构复制。

    (幸运的是,像byte[]这样的数组是引用类型,所以数组的内容已经通过引用传递了。)

    经常建议(例如在 Microsoft's Framework Design Guidelines 中)如果具有值类型语义的类型超过一定大小(32 字节),则应将其实现为引用类型,因此这个用例应该不是很经常。

  • 可变性。如果你希望一个方法能够改变传递给它的 struct 值,并且你希望调用者观察他的那个对象版本的改变,那么你需要通过引用传递(ref).如果值按值传递给方法,它会收到一个副本;改变副本将使原始对象保持不变。

    以上链接的Framework Design Guideline文章中也提到了这一点。

    请注意反对可变值类型的广泛建议(参见 "Why are mutable structs evil?")。您应该很少需要将 refout 参数与值类型一起使用。

  • COM interop 中所述,通常需要您声明 refout 参数。

从功能的角度来看,显式 ref 参数确实有用的情况很少。

我见过的一个例子:

public static void Swap<T>(ref T a, ref T b)
{
    var temp = a;
    a = b;
    b = temp;
}

例如,这种方法在某些排序算法中很有用。

有时使用 ref 参数的一个原因是作为一种优化技术。大型结构(值类型)有时会通过 ref 传递,即使被调用的方法无意修改结构的值,以避免复制结构内容。您可能会争辩说大型结构最好是 class(即引用类型),但是当您在周围保留大量此类对象时(例如在图形处理中),这有缺点。作为一种优化,这种技术不会提高代码的可读性,但在某些情况下会提高性能。

关于指针可以问类似的问题。 C# 通过 unsafefixed 关键字支持指针。从功能的角度来看几乎不需要它们:我真的想不出不使用指针就无法编码的功能。但是指针并不是用来使语言更具表现力或代码更具可读性,它们被用作低级优化技术。

事实上,通过引用传递结构确实是传递指向结构数据的指针的安全方式,这是大型结构的典型低级优化技术。

启用低级优化的功能是遗留功能吗?这可能取决于您的观点,但您不是唯一的 C# 用户。他们中的许多人会告诉您,指针和其他低级结构的可用性是 C# 相对于其他一些语言的一大优势。我个人认为 ref 参数不是遗留功能,它们是语言不可或缺的一部分,在某些情况下很有用。

这并不意味着您必须 使用 ref 参数,就像您不必使用 LINQ、async/await 或任何其他语言功能一样。当你需要它的时候,只要开心就好。