首页 文章

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

提问于
浏览
95

我有一个对象是我在程序的内存状态,还有一些其他的工作函数,我传递对象来修改状态 . 我已经通过ref传递给 Worker 函数了 . 但是我遇到了以下功能 .

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

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

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

    你使用refs的唯一地方是你要么:要传递一个大对象(即,对象/结构中有对象/结构到多个级别)并且复制它会很昂贵并且你正在调用一个框架, Windows API或其他需要它的API . 不要因为你能做到这一点 . 如果您开始更改参数中的值并且没有引起注意,您可以通过一些令人讨厌的错误获得帮助 .

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

  • 9

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

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

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

  • 3

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

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

  • -1

    简答:读我的article on argument passing .

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

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

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

    有用的答案: you almost never need to use ref/out . 它可能试图做太多 . 情况并非总是如此( TryParse 等是合理使用 out 的规范示例)但使用ref / out应该是相对罕见的 .

  • 0

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

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

    For multiple return values

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

    For primitives that change in a method as the result of the method call (方法对原始参数有副作用)

    • 在对象中实现方法作为实例方法,并在方法调用中操纵对象的状态(而不是参数)

    • 使用多重返回值解决方案并将返回值合并到您的状态

    • 创建一个对象,该对象包含可由方法操作的状态,并将该对象作为参数传递,而不是基元本身 .

  • 190

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

  • 26

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

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

  • 3

    接地零规则首先,在所涉及的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
    }
    

    我们可以说(有警告)非原始类型只是 Pointers 当我们通过ref传递它们时我们可以说我们正在通过 Double Pointer

相关问题