112

我有一个对象,它是程序的内存状态,还有一些其他工作函数,我将对象传递给它以修改状态。我一直通过 ref 将它传递给工作函数。但是我遇到了以下功能。

byte[] received_s = new byte[2048];
IPEndPoint tmpIpEndPoint = new IPEndPoint(IPAddress.Any, UdpPort_msg);
EndPoint remoteEP = (tmpIpEndPoint);

int sz = soUdp_msg.ReceiveFrom(received_s, ref remoteEP); 

它让我感到困惑,因为两者received_sremoteEP从函数返回内容。为什么remoteEP需要 aref而不需要received_s

我也是一名交流程序员,所以我无法从脑海中获取指针。

编辑:看起来 C# 中的对象是指向引擎盖下对象的指针。因此,当您将对象传递给函数时,您可以通过指针修改对象内容,并且传递给函数的唯一内容是指向对象的指针,因此不会复制对象本身。如果您希望能够在函数中切换或创建一个新对象,就像双指针一样,您可以使用 ref 或 out 。

4

8 回答 8

234

简短的回答:阅读我关于参数传递的文章

长答案:当引用类型参数按值传递时,只传递引用,而不传递对象的副本。这就像在 C 或 C++ 中传递一个指针(按值)。调用者不会看到参数值本身的变化,但看到引用指向的对象的变化。

当一个参数(任何类型的)通过引用传递时,这意味着调用者可以看到对参数的任何更改 - 对参数更改就是对变量的更改。

当然,这篇文章更详细地解释了所有这些:)

有用的答案:您几乎不需要使用 ref/out。它基本上是一种获取另一个返回值的方法,通常应该避免,因为这意味着该方法可能试图做太多事情。情况并非总是如此(TryParse等是合理使用的典型示例out),但使用 ref/out 应该是相对罕见的。

于 2009-03-11T19:11:07.743 回答
27

将非 ref 参数视为指针,将 ref 参数视为双指针。这对我帮助最大。

您几乎不应该通过 ref 传递值。我怀疑如果不是出于互操作问题,.Net 团队永远不会将它包含在原始规范中。处理 ref 参数解决的大多数问题的 OO 方法是:

对于多个返回值

  • 创建表示多个返回值的结构

对于由于方法调用而在方法中发生变化的基元(方法对基元参数有副作用)

  • 将对象中的方法实现为实例方法,并将对象的状态(而不是参数)作为方法调用的一部分进行操作
  • 使用多返回值解决方案并将返回值合并到您的状态
  • 创建一个包含可由方法操作的状态的对象,并将该对象作为参数传递,而不是原语本身。
于 2009-03-11T19:20:32.400 回答
13

您可能会编写一个完整的 C# 应用程序,并且永远不会通过 ref 传递任何对象/结构。

我有一位教授告诉我:

您唯一使用 refs 的地方是:

  1. 想要传递一个大对象(即对象/结构内部有对象/结构到多个级别)并且复制它会很昂贵并且
  2. 您正在调用需要它的框架、Windows API 或其他 API。

不要仅仅因为你可以就这样做。如果您开始更改参数中的值并且不注意,您可能会被一些讨厌的错误所困扰。

我同意他的建议,在我上学五年多的时间里,除了调用框架或 Windows API 之外,我从来没有需要它。

于 2009-03-11T20:06:07.860 回答
3

由于 received_s 是一个数组,因此您正在传递一个指向该数组的指针。该函数在适当位置操作现有数据,而不更改底层位置或指针。ref 关键字表示您将实际指针传递给该位置并在外部函数中更新该指针,因此外部函数中的值将会改变。

例如,字节数组是指向同一内存之前和之后的指针,内存刚刚更新。

Endpoint 引用实际上是将外部函数中指向 Endpoint 的指针更新为函数内部生成的新实例。

于 2009-03-11T19:10:53.110 回答
3

将 ref 视为您通过引用传递指针的意思。不使用 ref 意味着您正在按值传递指针。

更好的是,忽略我刚才所说的(这可能具有误导性,尤其是值类型)并阅读This MSDN page

于 2009-03-11T19:11:13.967 回答
1

虽然我总体上同意 Jon Skeet 的回答和其他一些答案,但使用 有一个用例ref,那就是加强性能优化。在性能分析过程中观察到,设置方法的返回值对性能有轻微影响,而ref将返回值用作参数会导致该轻微瓶颈被消除。

这只有在优化工作达到极端水平、牺牲可读性以及可能的可测试性和可维护性以节省毫秒或分毫秒的情况下才真正有用。

于 2018-04-25T20:04:41.357 回答
0

我的理解是,从 Object 类派生的所有对象都作为指针传递,而普通类型(int、struct)不作为指针传递并且需要引用。我不确定字符串(它最终是从 Object 类派生的吗?)

于 2012-02-23T09:30:54.757 回答
-1

零基础规则首先,在涉及的类型的上下文中,基元通过值(堆栈)传递,非基元通过引用(堆)传递。

涉及的参数默认通过Value传递。很好的帖子,详细解释了事情。 http://yoda.arachsys.com/csharp/parameters.html

Student myStudent = new Student {Name="A",RollNo=1};

ChangeName(myStudent);

static void ChangeName(Student s1)
{
  s1.Name = "Z"; // myStudent.Name will also change from A to Z
                // {AS s1 and myStudent both refers to same Heap(Memory)
                //Student being the non-Primitive type
}

ChangeNameVersion2(ref myStudent);
static void ChangeNameVersion2(ref Student s1)
{
  s1.Name = "Z"; // Not any difference {same as **ChangeName**}
}

static void ChangeNameVersion3(ref Student s1)
{
    s1 = new Student{Name="Champ"};

    // reference(myStudent) will also point toward this new Object having new memory
    // previous mystudent memory will be released as it is not pointed by any object
}

我们可以说(带有警告)非原始类型只不过是指针 当我们通过 ref 传递它们时,我们可以说我们正在传递双指针

于 2014-04-14T16:18:23.200 回答