何时使用ref以及在C#中何时不需要

时间:2009-03-11 19:08:01

标签: c# ref

我有一个对象是我在程序的内存状态,还有一些其他的工作函数,我传递对象来修改状态。我已经通过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需要refreceived_s不需要?

我也是一名程序员,因此我无法获得指针。

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

8 个答案:

答案 0 :(得分:198)

简短回答:阅读我的article on argument passing

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

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

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

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

答案 1 :(得分:26)

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

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

适用于多个返回值

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

对于因方法调用而导致方法发生变化的基元(方法对基本参数有副作用)

  • 在对象中实现方法作为实例方法,并在方法调用的一部分中操纵对象的状态(而不是参数)
  • 使用多重返回值解决方案并将返回值合并到您的州
  • 创建一个对象,该对象包含可由方法操作的状态,并将该对象作为参数传递,而不是原语本身。

答案 2 :(得分:9)

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

我有一位教授告诉我这件事:

  

您使用refs的唯一地方是:

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

    不要因为你可以这样做。有些人可以帮助你   如果您开始更改参数中的值而不是更改,则会出现令人讨厌的错误   注意。

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

答案 3 :(得分:3)

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

E.g。字节数组是指向同一内存的指针,内存刚刚更新。

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

答案 4 :(得分:3)

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

更好的是,忽略我刚才所说的内容(这可能会产生误导,特别是对于值类型)并阅读This MSDN page

答案 5 :(得分:0)

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

答案 6 :(得分:0)

虽然我同意Jon Skeet的总体答案以及其他一些答案,但有一个使用ref的用例,那就是收紧性能优化。在性能分析期间已经观察到设置方法的返回值具有轻微的性能影响,而使用ref作为参数,其中返回值被填充到该参数中导致这个轻微的瓶颈被移除。

只有在优化工作达到极限水平,牺牲可读性以及可测试性和可维护性以节省毫秒或者分裂毫秒时,这才真正有用。

答案 7 :(得分:-1)

接地零规则首先,在所涉及的TYPES环境中,基元通过值(堆栈)和非基元引用(堆)传递。

默认情况下,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传递它们时,我们可以说我们正在传递双指针