我正在创建一个需要传递对象的函数,以便可以通过函数对其进行修改。有什么区别:

public void myFunction(ref MyClass someClass)

public void myFunction(out MyClass someClass)

我应该使用哪个?为什么?

有帮助吗?

解决方案

ref 告诉编译器,该对象在输入函数之前是初始化的,而 out 告诉编译器,该对象将在函数内部初始化。

所以 ref 是双向的, out 仅限。

其他提示

ref 修饰符意味着:

  1. 该值已经设置了,并且
  2. 该方法可以读取并修改它。

out 修饰符意味着:

  1. 该值未设置,无法通过该方法读取 直到 它设置了。
  2. 方法 必须 在返回之前将其设置。

假设DOM出现在彼得的隔间,内容涉及有关TPS报告的备忘录。

如果DOM是裁判论点,他将有一份备忘录的印刷副本。

如果Dom是一个辩论,他会让彼得打印一份备忘录的新副本,供他随身携带。

我将尝试通过解释来尝试:

我认为我们了解该价值类型的工作原理吗?价值类型是(int,long,struct等)。当您将它们发送到没有ref命令的函数时,它会复制 数据. 。您对该功能中的数据做的任何事情都只会影响副本,而不是原始数据。 REF命令发送实际数据,任何更改都会影响功能之外的数据。

确定令人困惑的部分,参考类型:

让我们创建一个参考类型:

List<string> someobject = new List<string>()

当你新的时候 某些对象, ,创建了两个部分:

  1. 保存数据的内存块 某些对象.
  2. 该数据块的引用(指针)。

现在当您发送 某些对象 在没有ref的方法中,它复制了 参考 指针,而不是数据。因此,您现在拥有这个:

(outside method) reference1 => someobject
(inside method)  reference2 => someobject

两个指向同一对象的引用。如果您修改属性 某些对象 使用Reference2它将影响参考1指向的相同数据。

 (inside method)  reference2.Add("SomeString");
 (outside method) reference1[0] == "SomeString"   //this is true

如果将参考2删除或将其指向新数据,则不会影响Reference1或Data Reference1指向。

(inside method) reference2 = new List<string>();
(outside method) reference1 != null; reference1[0] == "SomeString" //this is true

The references are now pointing like this:
reference2 => new List<string>()
reference1 => someobject

现在发送时会发生什么 某些对象 通过参考方法?这 实际参考某些对象 被发送到该方法。因此,您现在只有一个对数据的引用:

(outside method) reference1 => someobject;
(inside method)  reference1 => someobject;

但是,这是什么意思?它的作用与除了两个主要内容外,与发送某些对象完全相同:

1)当您将方法内部的参考取消时,它将将方法除去该方法外部。

 (inside method)  reference1 = null;
 (outside method) reference1 == null;  //true

2)您现在可以将引用指向完全不同的数据位置,并且该功能外部的引用将指向新的数据位置。

 (inside method)  reference1 = new List<string>();
 (outside method) reference1.Count == 0; //this is true

裁判在 出去.

您应该使用 out 偏爱您的要求。

出去:

在C#中,方法只能返回一个值。如果您想返回多个值,则可以使用OUT关键字。 OUT修饰符返回为逐回报。最简单的答案是,“输出”的关键字用于从该方法中获取值。

  1. 您无需初始化调用函数中的值。
  2. 您必须在调用函数中分配值,否则编译器将报告错误。

参考:

在C#中,当您传递一个值类型(例如int,float,double等)作为对方法参数的参数时,它会按值传递。因此,如果修改参数值,它不会影响方法调用中的参数。但是,如果您用“参考”关键字标记参数,则将反映在实际变量中。

  1. 在调用该函数之前,您需要初始化变量。
  2. 在方法中的参考参数中分配任何值不是强制性的。如果您不更改该值,那么将其标记为“ Ref”是什么?

扩展狗,猫的例子。 REF的第二种方法更改了呼叫者引用的对象。因此,“猫” !!!

    public static void Foo()
    {
        MyClass myObject = new MyClass();
        myObject.Name = "Dog";
        Bar(myObject);
        Console.WriteLine(myObject.Name); // Writes "Dog". 
        Bar(ref myObject);
        Console.WriteLine(myObject.Name); // Writes "Cat". 
    }

    public static void Bar(MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

    public static void Bar(ref MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

由于您通过参考类型(类),因此无需使用 ref 因为每个默认值仅 参考 传递到实际对象,因此您始终更改引用背后的对象。

例子:

public void Foo()
{
    MyClass myObject = new MyClass();
    myObject.Name = "Dog";
    Bar(myObject);
    Console.WriteLine(myObject.Name); // Writes "Cat".
}

public void Bar(MyClass someObject)
{
    someObject.Name = "Cat";
}

只要您上课,就不必使用 ref 如果要更改方法中的对象。

refout 表现类似,除了以下差异。

  • ref 使用前必须初始化变量。 out 可以在没有分配的情况下使用变量
  • out 参数必须由使用它的函数视为未分配的值。因此,我们可以使用初始化 out 调用代码中的参数,但是当函数执行时,该值将丢失。

对于那些以榜样(像我一样)学习的人,这就是什么 安东尼·科列索夫(Anthony Kolesov)说.

我创建了一些最小的参考文献,外出和其他示例,以说明这一点。我没有涵盖最佳实践,而只是了解差异的例子。

https://gist.github.com/2upmedia/6d98a57b68d849ee7091

“贝克”

那是因为第一个将您的字符串引用更改为“贝克”。可以更改引用是可能的,因为您通过REF关键字传递了它(=>对字符串引用的引用)。第二个呼叫获得了对字符串的引用的副本。

首先,字符串看起来有些特别。但是字符串只是一个参考类,如果定义

string s = "Able";

然后s是对包含文本“ Able”的字符串类的引用!通过

s = "Baker";

不会更改原始字符串,而只是创建一个新实例,然后让S指向该实例!

您可以使用以下小型代码示例尝试:

string s = "Able";
string s2 = s;
s = "Baker";
Console.WriteLine(s2);

你能指望什么?您将获得的仍然是“能够”的,因为您只是将s中的引用设置为另一个实例,而S2则指向原始实例。

编辑:字符串也是不变的,这意味着根本没有修改现有字符串实例的方法或属性(您可以尝试在文档中找到一个,但不会罚款任何:-))。所有字符串操纵方法返回一个新的字符串实例! (这就是为什么您在使用StringBuilder类时通常会获得更好的性能的原因)

参考 表示已经设置了REF参数中的值,该方法可以读取并修改它。使用REF关键字与说呼叫者负责初始化参数的值相同。


出去 告诉编译器,对象的初始化是函数的责任,该函数必须分配给OUT参数。它不允许无分配。

出去: 返回语句可用于仅从函数返回一个值。但是,使用输出参数,您可以从函数返回两个值。输出参数就像参考参数一样,除了它们从方法中传递数据而不是将数据传输到该方法中。

以下示例说明了这一点:

using System;

namespace CalculatorApplication
{
   class NumberManipulator
   {
      public void getValue(out int x )
      {
         int temp = 5;
         x = temp;
      }

      static void Main(string[] args)
      {
         NumberManipulator n = new NumberManipulator();
         /* local variable definition */
         int a = 100;

         Console.WriteLine("Before method call, value of a : {0}", a);

         /* calling a function to get the value */
         n.getValue(out a);

         Console.WriteLine("After method call, value of a : {0}", a);
         Console.ReadLine();

      }
   }
}

参考:参考参数是对变量的存储位置的引用。当您通过引用传递参数时,与值参数不同,这些参数不会创建新的存储位置。参考参数代表与提供给该方法的实际参数相同的内存位置。

在C#中,您使用REF关键字声明参考参数。以下示例证明了这一点:

using System;
namespace CalculatorApplication
{
   class NumberManipulator
   {
      public void swap(ref int x, ref int y)
      {
         int temp;

         temp = x; /* save the value of x */
         x = y;   /* put y into x */
         y = temp; /* put temp into y */
       }

      static void Main(string[] args)
      {
         NumberManipulator n = new NumberManipulator();
         /* local variable definition */
         int a = 100;
         int b = 200;

         Console.WriteLine("Before swap, value of a : {0}", a);
         Console.WriteLine("Before swap, value of b : {0}", b);

         /* calling a function to swap the values */
         n.swap(ref a, ref b);

         Console.WriteLine("After swap, value of a : {0}", a);
         Console.WriteLine("After swap, value of b : {0}", b);

         Console.ReadLine();

      }
   }
}

参考和淘汰工作,就像通过参考文献传递并通过C ++中的指针传递。

对于参考,该论点必须声明和初始化。

出于淘汰,该论点必须声明,但可能会初始化或可能不会初始化

        double nbr = 6; // if not initialized we get error
        double dd = doit.square(ref nbr);

        double Half_nbr ; // fine as passed by out, but inside the calling  method you initialize it
        doit.math_routines(nbr, out Half_nbr);

创作时间:

(1)我们创建调用方法 Main()

(2)它创建列表对象(是参考类型对象)并将其存储在变量中 myList.

public sealed class Program 
{
    public static Main() 
    {
        List<int> myList = new List<int>();

在运行时:

(3)运行时在#00上分配存储器,足够宽以存储一个地址(#00 = myList, ,由于可变名称实际上只是内存位置的别名)

(4)运行时在内存位置在堆上创建列表对象#ff(例如,所有这些地址)

(5)然后运行时将对象的起始地址#ff存储在#00(或用文字存储列表对象的引用在指针中 myList)

回到创作时间:

(6)然后我们将列表对象作为参数传递 myParamList 到所谓的方法 modifyMyList 并为其分配一个新列表对象

List<int> myList = new List<int>();

List<int> newList = ModifyMyList(myList)

public List<int> ModifyMyList(List<int> myParamList){
     myParamList = new List<int>();
     return myParamList;
}

在运行时:

(7)运行时启动了调用方法的呼叫例程,作为其一部分,请检查参数的类型。

(8)找到参考类型后,它将在#04上分配一个内存,以使参数变量异化 myParamList.

(9)然后它也将值#ff存储在其中。

(10)运行时在内存位置上创建一个列表对象#004,并用此值替换#04(或在此方法中指向原始列表对象,并指向新列表对象)

#00中的地址没有更改,并保留对#FF的引用(或原始的 myList 指针没有打扰)。


参考 关键字是一项编译器指令,可以跳过(8)和(9)的运行时代码的生成,这意味着方法参数不会堆分配。它将使用原始的#00指针在#FF上的对象上操作。如果原始指针未初始化,运行时将停止抱怨,因为该变量未初始化

出去 关键字是一个编译器指令,与Ref几乎相同,在(9)和(10)上进行了轻微的修改。编译器期望该参数是非初始化的,并将继续(8),(4)和(5)在堆上创建一个对象,并将其起始地址存储在参数变量中。不会丢弃非传统错误,并且任何先前存储的参考文献都将丢失。

它们几乎相同 - 唯一的区别是,您传递的变量不需要初始化,并且使用REF参数的方法必须将其设置为某物。

int x;    Foo(out x); // OK 
int y;    Foo(ref y); // Error

REF参数用于可能会修改的数据,OUT参数用于数据,该数据是该函数(例如Int.TryParse)的附加输出,这些输出已经使用了某物的返回值。

下面我显示了一个示例,同时使用 参考出去. 。现在,大家都将被裁定和退出。

在下面提到的示例中,我发表评论 // myrefobj = new myClass {name =“ ref offe offeard nater !!”};行,会出现错误的说法 “使用未分配的本地变量'myrefobj'”, ,但是没有这样的错误 出去.

在哪里使用ref: :当我们调用具有IN参数的过程时,将使用相同的参数来存储该proc的输出。

在哪里使用: 当我们调用没有参数的过程时,将使用相同的参数来返回该proc的值。还要注意输出

public partial class refAndOutUse : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        myClass myRefObj;
        myRefObj = new myClass { Name = "ref outside called!!  <br/>" };
        myRefFunction(ref myRefObj);
        Response.Write(myRefObj.Name); //ref inside function

        myClass myOutObj;
        myOutFunction(out myOutObj);
        Response.Write(myOutObj.Name); //out inside function
    }

    void myRefFunction(ref myClass refObj)
    {
        refObj.Name = "ref inside function <br/>";
        Response.Write(refObj.Name); //ref inside function
    }
    void myOutFunction(out myClass outObj)
    {
        outObj = new myClass { Name = "out inside function <br/>" }; 
        Response.Write(outObj.Name); //out inside function
    }
}

public class myClass
{
    public string Name { get; set; }
} 
 public static void Main(string[] args)
    {
        //int a=10;
        //change(ref a);
        //Console.WriteLine(a);
        // Console.Read();

        int b;
        change2(out b);
        Console.WriteLine(b);
        Console.Read();
    }
    // static void change(ref int a)
    //{
    //    a = 20;
    //}

     static void change2(out int b)
     {
         b = 20;
     }

您可以检查此代码,当您使用“ ref”它的意思是您已经初始化该int/string时,它将描述您的完全不同

但是,当您使用“ OUT”时,它在两种情况下都可以使用Wheather U初始化该int/string,但您必须在该函数中初始化该int/string

参考:REF关键字用于将参数作为参考传递。这意味着,当该参数的值在方法中更改时,它会反映在调用方法中。使用REF关键字传递的参数必须在调用方法传递给调用方法之前初始化。

OUT:OUT关键字还用于传递REF关键字之类的参数,但是可以传递该参数而无需为其分配任何值。使用OUT关键字传递的参数必须在返回调用方法之前在调用方法中初始化。

public class Example
{
 public static void Main() 
 {
 int val1 = 0; //must be initialized 
 int val2; //optional

 Example1(ref val1);
 Console.WriteLine(val1); 

 Example2(out val2);
 Console.WriteLine(val2); 
 }

 static void Example1(ref int value) 
 {
 value = 1;
 }
 static void Example2(out int value) 
 {
 value = 2; 
 }
}

/* Output     1     2     

参考和方法超载

参考文献和外部都不能同时超载。但是,在运行时,参考和外出的处理方式有所不同,但在编译时间对其进行了相同的处理(CLR在两者创建IL用于Ref和Out时没有区分)。

从接收参数的方法的角度来看, refout 是C#要求方法必须写入每个方法 out 返回之前的参数,除了将其传递给 out 参数或写作,直到它被作为一个 out 参数到另一种方法或直接书写。请注意,其他一些语言不征得此类要求;一个虚拟或接口方法,在C#中声明 out 参数可以用另一种语言覆盖,该语言不会对此类参数施加任何特殊限制。

从呼叫者的角度来看,在许多情况下,C#在调用一种方法时会假设 out 参数将导致传递的变量写入而无需先读取。当调用其他语言编写的方法时,此假设可能是不正确的。例如:

struct MyStruct
{
   ...
   myStruct(IDictionary<int, MyStruct> d)
   {
     d.TryGetValue(23, out this);
   }
}

如果 myDictionary 识别 IDictionary<TKey,TValue> 以C#以外的语言编写的实施,即使 MyStruct s = new MyStruct(myDictionary); 看起来像是作业,它可能会离开 s 未修改。

请注意,与c#中的vb.net编写的构造函数没有任何假设 out 参数,无条件清除所有字段。上面提到的奇数行为不会完全用VB或C#中的代码编写,但是当C#呼叫代码写入VB.NET中编写的方法时,可能会发生。

如果要将参数传递为ref,则应在将参数传递给函数之前将其初始化,否则编译器本身将显示错误。但是,如果参数为OUT,则不需要在将其传递给对象参数之前初始化对象参数方法。您可以在调用方法本身中初始化对象。

除了允许您重新分配他人的变量到类的其他实例,返回多个值等, 使用 ref 或者 out 让别人知道您从他们那里需要什么以及您打算使用他们提供的变量

  • 不需要 ref 或者 out 如果您要做的就是修改事物 里面MyClass 参数中传递的实例 someClass.

    • 调用方法将看到更改 someClass.Message = "Hello World" 是否使用 ref, out 或无
    • 写作 someClass = new MyClass() 里面 myFunction(someClass) 换掉由 someClassmyFunction 仅方法。通话方法仍然知道原始 MyClass 实例它创建并传递给了您的方法
  • 需要 ref 或者 out 如果您打算交换 someClass 出现一个全新的对象,并希望调用方法查看您的更改

    • 写作 someClass = new MyClass() 里面 myFunction(out someClass) 更改通过调用的方法看到的对象 myFunction

存在其他程序员

他们想知道您将如何处理他们的数据。想象一下,您正在写一个将由数百万开发人员使用的库。您希望他们知道您打电话给您的变量时要做什么

  • 使用 ref 发表“在调用我的方法时将变量分配给某个值的变量。请注意,在我的方法过程中,我可能会完全将其更改为其他东西。不要指望您的变量在我受够了”

  • 使用 out 对“将占位符变量传递给我的方法。在打电话给我的方法之前 将要 到我完成的时候有所不同

顺便说一句,在C#7.2中有一个 in 修饰符也是如此

这会防止该方法将传递的实例交换为不同的实例。想想这是对数百万开发人员说的“传递您的原始变量参考,我保证不会将精心制作的数据换成其他内容”。 in 有一些特殊性,在某些情况下,例如可能需要隐性转换以使您的短期兼容 in int 编译器将暂时制作INT,使您的简短扩大,并通过参考并完成。它可以做到这一点,因为您已声明自己不会搞砸它。


微软与 .TryParse 数字类型的方法:

int i = 98234957;
bool success = int.TryParse("123", out i);

通过将参数标记为 out 他们在这里积极宣布:“我们是 确实 要改变您的98234957的精心制作价值,以获取其他东西”

当然,他们必须这样做,因为解析价值类型之类您正在创建的库:

public void PoorlyNamedMethod(out SomeClass x)

你可以看到这是一个 out, ,因此,您可以知道,如果您花费数小时来处理数字,创建完美的躯体:

SomeClass x = SpendHoursMakingMeAPerfectSomeClass();
//now give it to the library
PoorlyNamedMethod(out x);

好吧,这是浪费时间,花了所有这些时间来完成那个完美的课程。它肯定会被扔掉,并由不明的方法取代

请注意,该功能内部传递的参考参数直接处理。

例如,

    public class MyClass
    {
        public string Name { get; set; }
    }

    public void Foo()
    {
        MyClass myObject = new MyClass();
        myObject.Name = "Dog";
        Bar(myObject);
        Console.WriteLine(myObject.Name); // Writes "Dog".
    }

    public void Bar(MyClass someObject)
    {
        MyClass myTempObject = new MyClass();
        myTempObject.Name = "Cat";
        someObject = myTempObject;
    }

这会写狗,而不是猫。因此,您应该直接处理某些对象。

我可能不太擅长这一点,但是肯定会按值而不是参考来传递(即使在技术上是参考类型并且在堆上生活)?

        string a = "Hello";

        string b = "goodbye";

        b = a; //attempt to make b point to a, won't work.

        a = "testing";

        Console.WriteLine(b); //this will produce "hello", NOT "testing"!!!!

这就是为什么您需要参考,如果您希望更改在功能范围之外存在之外,则您不会传递参考。

据我所知,您只需要对结构/值类型和字符串本身需要参考,因为字符串是假装它的参考类型,但不是值类型。

不过,我可能在这里完全错了,我是新手。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top