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

When to use ref and when it is not necessary in C#

我有一个对象,它是我程序的内存状态,也有一些其他的辅助函数,我通过它来修改状态。我一直把它通过引用传递给工作者函数。不过,我遇到了以下功能。

1
2
3
4
5
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程序员,所以我在把指针从我的头脑中拿出来时遇到了问题。

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


简短的回答:阅读我关于论点传递的文章。

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

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

当然,这篇文章更详细地解释了这一切:)

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


把非引用参数看作指针,把引用参数看作双指针。这对我帮助最大。

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

对于多个返回值

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

对于因方法调用而在方法中更改的基元(方法对基元参数有副作用)

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


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

我有个教授告诉我:

The only place you'd use refs is where you either:

  • Want to pass a large object (ie, the objects/struct has
    objects/structs inside it to multiple levels) and copying it would
    be expensive and
  • You are calling a Framework, Windows API or other API that requires
    it.
  • Don't do it just because you can. You can get bit in the ass by some
    nasty bugs if you start changing the values in a param and aren't
    paying attention.

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


    把ref想象成一个引用传递一个指针。不使用ref意味着您正在按值传递指针。

    更好的是,忽略我刚才所说的(这可能会产生误导,尤其是对于值类型),并阅读这个msdn页面。


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

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

    端点引用实际上正在将指向外部函数中端点的指针更新为在函数内部生成的新实例。


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

    只有在优化工作达到极端水平时,这才真正有用,因为这样做会牺牲可读性,可能会牺牲可测试性和可维护性,从而节省毫秒或拆分毫秒。


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


    基本零规则首先,在涉及类型的上下文中,原语按值(堆栈)和非原语按引用(堆)传递。

    默认情况下,所涉及的参数按值传递。很好的文章能详细解释问题。网址:http://yoda.arachsys.com/csharp/parameters.html

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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
    }

    我们可以说(带有警告)非原语类型只是指针当我们通过裁判时,我们可以说我们是通过了双指针