ChatGPT解决这个技术问题 Extra ChatGPT

'ref' 和 'out' 关键字有什么区别?

我正在创建一个函数,我需要在其中传递一个对象,以便该函数可以修改它。有什么区别:

public void myFunction(ref MyClass someClass)

public void myFunction(out MyClass someClass)

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

你:我需要传递一个对象以便可以修改它 看起来 MyClass 将是一个 class 类型,即引用类型。在这种情况下,即使没有 ref/out 关键字,您传递的对象也可以由 myFunction 修改。 myFunction 将接收一个指向 same 对象的 new 引用,并且它可以根据需要修改同一个对象。 ref 关键字的不同之处在于,myFunction 收到了对同一对象的 same 引用。仅当 myFunction 将引用更改为指向 另一个 对象时,这才是重要的。
当@AnthonyKolesov 的答案非常完美时,我对这里的大量令人困惑的答案感到困惑。
当您希望方法返回多个值时,声明 out 方法很有用。可以将一个参数分配给 null。这使方法能够有选择地返回值。
这里用Example解释它更容易理解:) dotnet-tricks.com/Tutorial/csharp/…
从技术上讲,@JeppeStigNielsen 的评论是对 OP 实际问题的(唯一)正确答案。要将对象传递给方法以便方法可以修改对象,只需将(对对象的引用)按值传递给方法。通过 object 参数更改方法中的对象会修改原始对象,即使该方法包含其自己的单独变量(引用同一对象)。

m
miguel

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

因此,虽然 ref 是双向的,但 out 是唯一的。


out 的另一件很酷的事情是该函数必须分配给 out 参数。不允许将其保留为未分配状态。
'ref' 是否仅适用于值类型?由于引用类型总是通过 ref 传递。
是的。值类型,包括结构
@faulty:不, ref 不仅适用于值类型。 ref/out 就像 C/C++ 中的指针,它们处理对象的内存位置(在 C# 中间接)而不是直接对象。
@faulty:与直觉相反,引用类型在 C# 中始终按值传递,除非您使用 ref 说明符。如果设置 myval=somenewval,则效果仅在该函数范围内。 ref 关键字将允许您将 myval 更改为指向 somenewval。
A
Agi Hammerthief

ref 修饰符意味着:

该值已设置并且该方法可以读取和修改它。

out 修饰符意味着:

该值未设置,并且在设置之前无法由该方法读取。该方法必须在返回之前设置它。


这个答案最清楚和简洁地解释了编译器在使用 out 关键字而不是 ref 关键字时施加的限制。
来自 MSDN: ref 参数必须在使用前初始化,而 out 参数在传递之前不必显式初始化,并且任何先前的值都将被忽略。
使用 out,如果它在方法被调用之前已被初始化,是否可以在该方法设置之前在方法内读取它?我的意思是,被调用的方法可以读取调用方法作为参数传递给它的内容吗?
Panzercrisis,对于“out”,如果已经设置,调用的方法可以读取。但它必须重新设置。
M
Michael Blackburn

假设 Dom 出现在 Peter 的隔间,内容是关于 TPS 报告的备忘录。

如果 Dom 是一个 ref 参数,他将有一份备忘录的打印副本。

如果 Dom 是个吵架者,他会让 Peter 打印一份新的备忘录,让他随身携带。


ref Dom 会用铅笔写报告,以便彼得可以修改它
@Deebster 你知道,那个比喻对你没有任何作用,你为什么要这样折磨它? ;)
有趣但有教育意义,stackoverflow 需要更多这样的帖子
万一有人觉得这个答案只有一半好笑,请观看电影“办公空间”。
一个很好的解释。这对于难以理解 C# 核心概念的学生非常有帮助。保持 :-)
J
James Roland

我将尝试解释:

我认为我们了解值类型如何正确工作?值类型是(int、long、struct 等)。当您将它们发送到没有 ref 命令的函数时,它会复制数据。您对函数中的数据所做的任何事情都只会影响副本,而不是原始数据。 ref 命令发送 ACTUAL 数据,任何更改都会影响函数外的数据。

好的,进入令人困惑的部分,引用类型:

让我们创建一个引用类型:

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

当您新建某个对象时,会创建两个部分:

为某个对象保存数据的内存块。对该数据块的引用(指针)。

现在,当您将 someobject 发送到没有 ref 的方法时,它会复制引用指针,而不是数据。所以你现在有这个:

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

指向同一个对象的两个引用。如果您使用 reference2 修改某个对象的属性,它将影响 reference1 指向的相同数据。

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

如果您将 reference2 清空或将其指向新数据,则不会影响 reference1 也不会影响 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

现在,当您通过 ref 将 someobject 发送给方法时会发生什么?对 someobject 的实际引用被发送到该方法。所以你现在只有一个对数据的引用:

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

但是,这是什么意思?它的作用与不通过 ref 发送 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

你的意思毕竟(在参考情况下)只有一个对数据的引用,但它有两个别名。正确的?
赞成明确的解释。但我认为这并不能回答问题,因为它没有解释 refout 参数之间的区别。
惊人。你能解释一下out关键字吗?
R
Ruben Bartelink

ref is in and out

只要满足您的要求,您就应该优先使用 out


不完全是,作为接受的答案参考如果定向和无用的忽略值类型,如果没有传回。
@kenny:您能否澄清一下-即,您会更改哪些词以保持答案的精神,但消除您所感知的不准确之处?我的回答不是新手的疯狂猜测,但您评论中的匆忙(简洁,错别字)似乎假设它是。目的是提供一种用最少的单词来思考差异的方法。
(顺便说一句,我熟悉值类型、引用类型、按引用传递、按值传递、COM 和 C++,如果您发现在说明中引用这些概念很有用)
对象引用按值传递(使用“ref”或“out”关键字时除外)。将对象视为 ID 号。如果一个类变量包含“Object #1943”,并且将该变量按值传递给例程,则该例程可以对 Object #1943 进行更改,但它不能使变量指向“Object #1943”以外的任何内容。如果变量是通过引用传递的,则例程可以使变量点保持“对象#5441”。
@supercat:我确实喜欢您对 ref vs val 的解释(以及此后续类比)。我认为肯尼实际上不需要向他解释任何这些,(相对)他的评论令人困惑。我真希望我们都可以删除这些该死的评论,尽管它们只是让每个人都感到困惑。所有这些废话的根本原因似乎是肯尼误读了我的答案,并且还没有指出应该添加/删除/替换的单个单词。我们三个人都没有从我们不知道的讨论中学到任何东西,而另一个答案的赞成票数量可笑。
N
Nazmul Hasan

出去:

在 C# 中,一个方法只能返回一个值。如果你想返回多个值,你可以使用 out 关键字。 out 修饰符以引用返回的形式返回。最简单的答案是关键字“out”用于从方法中获取值。

您不需要在调用函数中初始化值。必须在被调用函数中赋值,否则编译器会报错。

参考:

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

您需要在调用函数之前初始化变量。不必为方法中的 ref 参数分配任何值。如果不改变值,有什么必要将其标记为“ref”?


“在 C# 中,一个方法只能返回一个值。如果你想返回多个值,可以使用 out 关键字。”我们也可以使用“ref”来返回值。那么如果我们想从一个方法返回多个值,我们可以同时使用 ref 和 out 吗?
在 c# 7 中,您可以使用 ValueTuples 返回多个值。
R
Richard Bos

扩展狗,猫的例子。带有 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;
    }

F
Farhan S.

ref 表示 ref 参数中的值已经设置,方法可以读取和修改它。使用 ref 关键字相当于说调用者负责初始化参数的值。

out 告诉编译器对象的初始化是函数的责任,函数必须分配给 out 参数。不允许将其保留为未分配状态。

https://www.codemaggot.com/ref-and-out-keywords/


g
gmail user

refout 的行为相似,但有以下区别。

ref 变量必须在使用前初始化。 out 变量可以在没有赋值的情况下使用

out 参数必须被使用它的函数视为未分配的值。因此,我们可以在调用代码中使用初始化的 out 参数,但是当函数执行时该值会丢失。


C
Community

对于那些通过示例学习的人(比如我),这里是 Anthony Kolesov is saying

我已经创建了一些 ref、out 和其他的最小示例来说明这一点。我没有介绍最佳实践,只是举例来了解差异。

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


A
Albic

由于您传递的是引用类型(一个类),因此无需使用 ref,因为默认情况下只传递了对实际对象的 reference,因此您总是更改引用后面的对象.

例子:

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


这仅在没有创建和返回新对象时才有效。创建新对象时,对旧对象的引用将丢失。
这是错误的 - 请尝试以下操作:将 someObject = null 添加到 Bar 结束执行。您的代码将运行良好,因为只有 Bar 对实例的引用被取消。现在将 Bar 更改为 Bar(ref MyClass someObject) 并再次执行 - 您将获得 NullReferenceException,因为 Foo 对实例的引用也已为空。
m
mmmmmmmm

“贝克”

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

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

string s = "Able";

那么 s 是对包含文本“Able”的字符串类的引用!通过对同一变量的另一个赋值

s = "Baker";

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

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

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

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

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


确切地。因此,严格地说“由于您传递的是引用类型(类),因此不需要使用 ref”,这并不完全正确。
从理论上讲,这样说是正确的,因为他写了“以便可以修改”,这在字符串上是不可能的。但是由于不可变对象,“ref”和“out”对于引用类型也非常有用! (.Net 包含很多不可变的类!)
你是对的。我没有想到像字符串这样的不可变对象,因为大多数对象都是可变的。
好吧,可以肯定的是,这是在 LQP 中看到的一个令人费解的答案;它没有任何问题,只是它似乎是对另一条评论的冗长而彻底的回应(因为原始问题在其修订中都没有提到 Able 和 Baker),就好像这是一个论坛。我想这在很久以前还没有真正解决。
s
snr

对于那些寻找简洁答案的人。

ref 和 out 关键字都用于传递引用。 ref 关键字的变量必须有一个值,或者必须在传递之前引用一个对象或 null。与 ref 不同,out 关键字的变量在传递后必须有值或必须引用对象或 null,并且在传递之前不需要有值或引用对象。


F
Faisal Naseer

Out:return 语句可用于从函数中仅返回一个值。但是,使用输出参数,您可以从函数返回两个值。输出参数与引用参数类似,不同之处在于它们将数据从方法中传输出来而不是传入方法中。

以下示例说明了这一点:

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();

      }
   }
}

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

在 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
Caius Jard

除了允许您将其他人的变量重新分配给类的不同实例、返回多个值等之外,使用 refout 还可以让其他人知道您需要他们做什么以及您打算做什么他们提供的变量

如果您要做的只是修改在参数 someClass 中传递的 MyClass 实例中的内容,则不需要 ref 或 out。调用方法将看到类似 someClass.Message = "Hello World" 的变化,无论你使用 ref、out 还是什么都没有 在 myFunction(someClass) 中写入 someClass = new MyClass() 会换出 myFunction 方法范围内 someClass 看到的对象只要。调用方法仍然知道它创建并传递给您的方法的原始 MyClass 实例

调用方法将看到类似 someClass.Message = "Hello World" 的变化,无论您使用 ref、out 还是什么都没有

在 myFunction(someClass) 中写入 someClass = new MyClass() 仅在 myFunction 方法的范围内换出 someClass 看到的对象。调用方法仍然知道它创建并传递给您的方法的原始 MyClass 实例

如果您计划将 someClass 换成一个全新的对象并希望调用方法看到您的更改,则需要 ref 或 out 在 myFunction(out someClass) 中写入 someClass = new MyClass() 会更改调用 myFunction 的方法看到的对象

在 myFunction(out someClass) 中写入 someClass = new MyClass() 会更改调用 myFunction 的方法看到的对象

存在其他程序员

他们想知道你将如何处理他们的数据。想象一下,您正在编写一个将被数百万开发人员使用的库。当他们调用您的方法时,您希望他们知道您将如何处理他们的变量

使用 ref 声明“在调用我的方法时传递分配给某个值的变量。请注意,我可能会在我的方法过程中完全将其更改为其他内容。不要指望你的变量指向旧的完成后反对”

使用 out 声明“将占位符变量传递给我的方法。它是否有值无关紧要;编译器会强制我将其分配给新值。我绝对保证指向的对象你调用我的方法之前的变量,在我完成时会有所不同

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

这可以防止该方法将传入的实例换成不同的实例。可以将其想象为对数百万开发人员说“将您的原始变量引用传递给我,我保证不会将您精心制作的数据换成其他东西”。 in 有一些特殊性,在某些情况下,例如可能需要隐式转换以使您的 short 与 in int 兼容,编译器将临时生成一个 int,将您的 short 扩展为它,通过引用传递它并完成向上。它可以做到这一点,因为你已经宣布你不会搞砸它。

Microsoft 使用数字类型的 .TryParse 方法做到了这一点:

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

通过将参数标记为 out,他们在此处积极声明“我们肯定会将您精心设计的 98234957 值更改为其他值”

当然,对于解析值类型之类的事情,他们有点不得不这样做,因为如果不允许解析方法将值类型换成其他东西,它就不会很好地工作。但是想象一下,有些方法中有一些虚构的方法您正在创建的库:

public void PoorlyNamedMethod(out SomeClass x)

您可以看到它是一个 out,因此您可以知道,如果您花费数小时处理数字,则可以创建完美的 SomeClass:

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

好吧,那是浪费时间,花所有这些时间来制作完美的课程。它肯定会被扔掉,取而代之的是PoorlyNamedMethod


R
RotatingWheel

ref 和 out 就像 C++ 中的引用传递和指针传递一样工作。

对于 ref,参数必须声明和初始化。

对于 out,参数必须声明,但可能会或可能不会被初始化

        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);

您可以内联声明一个变量:out double Half_nbr
C
Community

创作时间:

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

(2) 它创建一个 List 对象(它是一个引用类型的对象)并将其存储在变量 myList 中。

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

在运行时:

(3) 运行时在 #00 的堆栈上分配内存,足够宽以存储地址(#00 = myList,因为变量名实际上只是内存位置的别名)

(4)运行时在内存位置#FF的堆上创建一个列表对象(所有这些地址都是为了举例)

(5) 然后运行时将对象的起始地址#FF 存储在#00 处(或者换句话说,将 List 对象的引用存储在指针 myList 中)

返回创作时间:

(6) 然后我们将 List 对象作为参数 myParamList 传递给被调用的方法 modifyMyList 并为其分配一个新的 List 对象

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的堆上创建一个list对象,并用这个值替换#04中的#FF(或者在这个方法中取消引用原来的List对象并指向新的List对象)

#00 中的地址未更改并保留对#FF 的引用(或原始 myList 指针不受影响)。

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

out 关键字是一个编译器指令,它与 ref 几乎相同,只是在 (9) 和 (10) 处稍作修改。编译器希望参数未初始化,并将继续使用 (8)、(4) 和 (5) 在堆上创建对象并将其起始地址存储在参数变量中。不会抛出未初始化的错误,并且之前存储的任何引用都将丢失。


D
Dietrich Baumgarten

为了说明许多出色的解释,我开发了以下控制台应用程序:

using System;
using System.Collections.Generic;

namespace CSharpDemos
{
  class Program
  {
    static void Main(string[] args)
    {
      List<string> StringList = new List<string> { "Hello" };
      List<string> StringListRef = new List<string> { "Hallo" };

      AppendWorld(StringList);
      Console.WriteLine(StringList[0] + StringList[1]);

      HalloWelt(ref StringListRef);
      Console.WriteLine(StringListRef[0] + StringListRef[1]);

      CiaoMondo(out List<string> StringListOut);
      Console.WriteLine(StringListOut[0] + StringListOut[1]);
    }

    static void AppendWorld(List<string> LiStri)
    {
      LiStri.Add(" World!");
      LiStri = new List<string> { "¡Hola", " Mundo!" };
      Console.WriteLine(LiStri[0] + LiStri[1]);
    }

    static void HalloWelt(ref List<string> LiStriRef)
     { LiStriRef = new List<string> { LiStriRef[0], " Welt!" }; }

    static void CiaoMondo(out List<string> LiStriOut)
     { LiStriOut = new List<string> { "Ciao", " Mondo!" }; }
   }
}
/*Output:
¡Hola Mundo!
Hello World!
Hallo Welt!
Ciao Mondo!
*/

AppendWorld:传递了一个名为 LiStri 的 StringList 副本。在方法开始时,此副本引用原始列表,因此可用于修改此列表。后来 LiStri 在方法中引用了另一个不影响原始列表的 List 对象。

HalloWelt:LiStriRef 是已经初始化的 ListStringRef 的别名。传递的 List 对象用于初始化一个新对象,因此 ref 是必要的。

CiaoMondo:LiStriOut 是 ListStringOut 的别名,必须初始化。

所以,如果一个方法只是修改了被传递变量引用的对象,编译器不会让你使用 out 并且你不应该使用 ref 因为它不会混淆编译器而是代码的读者。如果该方法将使传递的参数引用另一个对象,则将 ref 用于已初始化的对象,将 out 用于必须为传递的参数初始化新对象的方法。除此之外,refout 的行为相同。


T
Talha Khan

它们几乎相同——唯一的区别是作为输出参数传递的变量不需要初始化,使用 ref 参数的方法必须将其设置为某个值。

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

Ref 参数用于可能被修改的数据,out 参数用于作为函数(例如 int.TryParse)的附加输出的数据,这些数据已经在使用返回值。


D
Dejan Ciev

Ref: 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     

方法重载中的引用和输出

ref 和 out 不能同时用于方法重载。但是, ref 和 out 在运行时的处理方式不同,但在编译时它们的处理方式相同(CLR 在为 ref 和 out 创建 IL 时不会区分两者)。


k
kletnoe

下面我展示了一个同时使用 Ref 和 out 的示例。现在,你们都将被清除关于 ref 和 out 的信息。

在下面提到的示例中,当我评论 //myRefObj = new myClass { Name = "ref outside called!! " };行,会得到一个错误,说“使用未分配的局部变量'myRefObj'”,但输出中没有这样的错误。

在哪里使用 Ref:当我们使用 in 参数调用过程时,将使用相同的参数来存储该过程的输出。

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

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; }
} 

H
Haris Zia
 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”时,它在两种情况下都有效,无论您是否初始化该 int/string,但您必须在该函数中初始化该 int/string


Z
Zvi Mints

我想举例说明两个主要区别:

ref 和 out 通过引用传递,hense;

 class Program
    {
        public static void Main(string[] args)
        {
            var original = new ObjectWithMememberList(3);
            Console.WriteLine(original.MyList.Capacity); // 3
            ChangeList(original.MyList);
            Console.WriteLine(original.MyList.Capacity); // 3
        }

        static void ChangeList(List<int> vr)
        {
            vr = new List<int>(2);
        }
}

但:

 class Program
    {
        public static void Main(string[] args)
        {
            var original = new ObjectWithMememberList(3);
            Console.WriteLine(original.MyList.Capacity); // 3
            ChangeList(ref original.MyList);
            Console.WriteLine(original.MyList.Capacity); // 2
        }

        static void ChangeList(ref List<int> vr)
        {
            vr = new List<int>(2);
        }
}

out 相同。 2. ref 参数必须是可赋值变量。感觉:

ChangeList(ref new List<int>()); // Compile Error [might not be initialized before accessing]

但:

List<int> xs;
ChangeList(out xs); // Compiles

s
supercat

从接收参数的方法的角度来看,refout 之间的区别在于 C# 要求方法必须在返回之前写入每个 out 参数,并且不能对这样的参数做任何事情,除了将它作为 out 参数传递或写入它,直到它作为 out 参数传递给另一个方法或直接写入。请注意,其他一些语言没有这样的要求;在 C# 中使用 out 参数声明的虚拟或接口方法可以用另一种语言覆盖,该语言不对此类参数施加任何特殊限制。

从调用者的角度来看,在许多情况下,C# 会假设在调用带有 out 参数的方法时,会导致在未先读取之前写入传递的变量。当调用用其他语言编写的方法时,这种假设可能不正确。例如:

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

如果 myDictionary 识别出使用非 C# 语言编写的 IDictionary<TKey,TValue> 实现,即使 MyStruct s = new MyStruct(myDictionary); 看起来像一个赋值,它也可能使 s 保持不变。

请注意,用 VB.NET 编写的构造函数与 C# 中的构造函数不同,它不会假设被调用的方法是否会修改任何 out 参数,并且会无条件地清除所有字段。上面提到的奇怪行为不会发生在完全用 VB 或完全用 C# 编写的代码中,但是当用 C# 编写的代码调用用 VB.NET 编写的方法时可能会发生。


R
Rakeshkumar Das

如果要将参数作为 ref 传递,则应在将参数传递给函数之前对其进行初始化,否则编译器本身将显示错误。但在 out 参数的情况下,您无需在将对象参数传递给之前初始化它方法。您可以在调用方法本身中初始化对象。


P
Peter Mortensen

请注意,在函数内部传递的引用参数是直接处理的。

例如,

    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;
    }

这将写狗,而不是猫。因此,您应该直接在 someObject 上工作。


虽然这里的一切都非常正确,但它并不能真正解释通过引用或输出的值之间的区别。充其量它解释了引用和值/不可变类型之间的区别。
如果您希望该代码编写 cat ,请将该对象与“ ref ”键一起传递,如下所示: public static void Bar(ref MyClass someObject), Bar(ref myObject);
E
Edwin

我可能不太擅长这个,但是字符串(即使它们在技术上是引用类型并且存在于堆上)肯定是按值传递的,而不是引用?

        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"!!!!

这就是为什么您需要 ref 如果您希望更改存在于创建它们的函数范围之外,否则您不会传递引用。

据我所知,您只需要 ref 结构/值类型和字符串本身,因为字符串是一种假装它是但不是值类型的引用类型。

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


欢迎来到 Stack Overflow,埃德温。据我所知,字符串是通过引用传递的,就像任何其他对象一样。您可能会感到困惑,因为字符串是不可变的对象,因此它们通过引用传递并不那么明显。想象一下,字符串有一个名为 Capitalize() 的方法,可以将字符串的内容更改为大写字母。如果您随后将行 a = "testing"; 替换为 a.Capitalize();,那么您的输出将是“HELLO”,而不是“Hello”。不可变类型的优点之一是您可以传递引用,而不必担心其他代码会更改值。
一个类型可以公开三种基本的语义类型:可变引用语义、可变值语义和不可变语义。考虑具有字段或属性 m 的类型 T 的变量 x 和 y,并假设 x 被复制到 y。如果 T 具有引用语义,则 ym 将观察到对 xm 的更改 如果 T 具有值语义,则可以更改 xm 而不会影响 ym 如果 T 具有不可变语义,则 xm 和 ym 都不会改变。不可变语义可以通过引用或值对象来模拟。字符串是不可变的引用对象。