何时使用ref,何时不需要在C#中
我有一个对象,是我在程序的内存状态,也有一些其他工作人员的function,我传递对象来修改状态。 我一直在通过工人职能通过它。 但是我碰到以下function。
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_s
和remoteEP
正在从函数中返回东西。 为什么remoteEP
需要一个ref
并且received_s
不需要?
我也是ac程序员,所以我有一个问题得到指出我的头。
编辑:它看起来像在C#中的对象是指向引擎盖下的对象的指针。 所以当你传递一个对象到一个函数时,你可以通过指针修改对象内容,唯一传递给函数的是指向对象的指针,所以对象本身不被复制。 如果你希望能够切换或者在函数中创build一个类似于双指针的新对象,你可以使用ref或者out。
简短的回答:阅读我关于论证传递的文章 。
长答案:当一个引用types参数被值传递时,只传递引用, 而不是对象的副本。 这就像在C或C ++中传递一个指针(按值)。 调用者不会看到参数本身值的变化,但是参考指向的对象的变化将被看到。
当一个参数(任何types)通过引用传递时,这意味着调用者可以看到参数的任何变化 – 参数的变化是variables的变化。
文章当然更详细地解释了这一切:)
有用的答案: 你几乎从不需要使用ref / out 。 这基本上是一种获得另一个返回值的方法,通常应该避免,因为这意味着该方法可能会尝试做太多。 情况并非总是如此( TryParse
等是合理使用的典型例子),但是使用ref / out应该是相对罕见的。
将一个非参数参数看作是一个指针,将一个参数指定为一个双指针。 这对我帮助最大。
你几乎不应该通过ref传递值。 我怀疑,如果不是为了互相关心,那么.Net团队绝不会把它包含在原来的规范中。 处理参考参数解决的大部分问题的OO方法是:
对于多个返回值
- 创build表示多个返回值的结构
对于作为方法调用结果而改变方法的原语(方法对原始参数有副作用)
- 在对象中实现方法作为实例方法,并在方法调用中操作对象的状态(而不是参数)
- 使用多个返回值解决scheme并将返回值合并到您的状态
- 创build一个包含状态的对象,该状态可以由方法操作,并将该对象作为parameter passing,而不是基元本身。
你可能可以编写一个完整的C#应用程序,并且绝不会传递任何对象/结构体。
我有一位教授告诉我这个:
你唯一可以使用的地方是你:
- 想要传递一个大的对象(即对象/结构中有对象/结构的内部到多个层次),复制它将是昂贵的
- 您正在调用一个框架,Windows API或其他需要它的API。
不要只因为可以。 如果您开始更改参数中的值并且没有注意,您可以通过一些令人讨厌的错误获得一些帮助。
我同意他的build议,在我上学五年多的时间里,除了调用Framework或Windows API之外,我从来没有这样的需求。
由于received_s是一个数组,所以你传递一个指向该数组的指针。 该函数操作现有的数据,而不是改变底层位置或指针。 ref关键字表示您将实际指针传递到位置并更新外部函数中的指针,所以外部函数中的值将会更改。
例如,字节数组是前后相同内存的指针,内存刚更新。
端点引用实际上将外部函数中的端点指针更新为函数内部生成的新实例。
把ref看作是你通过引用传递一个指针。 不使用引用意味着你正在传递一个值的指针。
更好的是,忽略我刚才所说的(这可能是误导,尤其是值types),并阅读此MSDN页面 。
我的理解是,从Object类派生的所有对象都作为指针传递,而普通types(int,struct)不作为指针传递并要求ref。 我也不确定string(最终是从Object类派生的吗?)
首先是基本零规则,在所涉及的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 }
我们可以说(警告)非原始types只是指针当我们通过ref传递它们时,我们可以说我们传递了双指针