使用REF和OUT关键字在C#中按值传递和传递值

这是我到目前为止所理解的:

通过价值

按值传递意味着传递参数的副本。 对该副本的更改不会更改原始副本。

通过参考

通过引用传递意味着传递对原始的引用。 对引用的更改会影响原始引用。

REF关键字

REF告诉编译器在进入函数之前初始化对象。 REF表示该值已经设置,因此该方法可以读取并修改它。 REF有两种方式,包括进出。

OUT关键字

OUT告诉编译器该对象将在函数内初始化。 OUT表示该值尚未设置,因此必须在调用return之前设置。 OUT只是一种方式,即出局。

那么在什么情况下你会结合使用ref和out关键字,通过引用传递还是通过值传递? 例子会有很大帮助。

非常感谢。

永远不会在1参数上组合refout 。 它们都意味着“通过引用传递”。

您当然可以在一种方法中组合ref参数和输出参数。

refout之间的区别主要在于意图 。 ref信号双向数据传输,out表示单向。

但除了意图之外,C#编译器跟踪明确赋值,这是最显着的差异。 它还可以防止误读(读取)out参数。

 void SetOne(out int x) { int y = x + 1; // error, 'x' not definitely assigned. x = 1; // mandatory to assign something } void AddTwo(ref int x) { x = x + 2; // OK, x is known to be assigned } void Main() { int foo, bar; SetOne(out foo); // OK, foo does not have to be assigned AddTwo(ref foo); // OK, foo assigned by SetOne AddTwo(ref bar); // error, bar is unassigned } 

非常感谢

通过正确和谨慎地使用语言,您的理解将得到改善。

按值传递意味着传递参数的副本。

是的,这完全准确。

对该副本的更改不会更改原始副本。

不完全是。 首先要仔细区分值变量 。 考虑:

 class Foo { public int x; } ... void N() { Foo blah = new Foo(); blah.x = 0; M(blah); } ... void M(Foo foo) { foo.x = 123; // changes blah.x foo = null; // does not change blah } 

这里的变量是x,blah和foo。 x是一个字段,blah是本地的,foo是一个forms参数。

这里的值为null,0,123,以及对Foo实例的引用。 该引用是一个值。 了解这一事实至关重要。

通过将变量blah的值复制到变量foo中来传递blah值的副本。 blah的值是对Foo实例的引用。

M可以改变变量x的值,因为M具有blah值的副本,它是对Foo的引用。 当M将foo的内容更改为null时,这不会改变blah; foo包含blah值的副本。

通过引用传递意味着传递对原始的引用。

仔细选择你的措辞。 什么是“原始”?

这将更好地说明为“通过引用传递意味着传递对参数的引用,该参数必须是变量”。 考虑它的一种更简单的方法是“通过引用传递使参数成为作为参数传递的变量的别名”。

对引用的更改会影响原始引用。

由于参数和参数是彼此的别名,因此引用的更改不会影响原始参数; 参考文献是原文。 它们都是相同的变量

REF告诉编译器在进入函数之前初始化对象。

“对象”毫无意义。 你的意思是“变量”。

“ref”不“告诉编译器变量已初始化”。 相反,“ref”告诉编译器“你,编译器,必须validation变量是否已初始化”。 那是完全不同的!

REF表示该值已设置,

不,ref要求已设置变量 。 没有“设定价值”这样的东西。

因此,该方法可以读取并修改它。

“it”的意思是“变量”。

REF有两种方式,包括进出。

正确。

OUT告诉编译器该对象将在函数内初始化。

停止使用“对象”来表示“变量”。 如果你不再混淆完全不同的东西,你会更清楚地理解事物。 变量不是对象。 变量是存储位置 ,其中一些可能包含 ,其中一些值可能是对象的引用

因此,out告诉编译器变量将在方法内初始化,是的,但这不太对。 您忘记了该方法将引发exception的情况,或者该方法将进入无限循环 – 这些也是合法的方案。

OUT表示该值尚未设置,

同样,通过“值”,您的意思是“变量”。 但这不准确。 将初始化变量作为“out”参数传递是完全合法的。 毫无意义,但合法。

因此必须在调用return之前设置。

没有叫“回归”; 方法被称为。 但是,是的,该方法必须在正常返回之前为变量赋值。

OUT只是一种方式,即出局。

对。

那么在什么情况下你会结合使用ref和out关键字

没有这样的场景。

你理解通过任何一种方式的动态。 一些参数场景可能是:

  • ref int num用于输入/输出参数。 该函数可以修改其中的值。
  • out int num like ref除了函数必须在返回之前为它赋值。

通常,输出参数适用于函数必须返回多个值时,因为函数只有一个返回值(尽管它可以是复合值)。

有时,数据访问提供程序(如某些ADO.NET方法)使用输出参数来传递信息。 一些数据访问方法模仿具有输入/输出参数的数据库存储过程。

编辑:引用类型的一个规定是参数ref StringBuilder wordStringBuilder word (按值)表现相同 – 外部字符串受到影响,尽管底层实现可能略有不同,因为在那一点焦点是引用而不是值在堆上。

编辑:我已经更正了这个答案,以反映C#中的’out’关键字没有达到预期的效果(例如计算机科学意义上的真正OUT参数)。 我最初声称’out’是通过OUT值传递但被certificate是错误的。

你不能一起使用’out’和’ref’。 C#(.NET Framework)中有三种调用约定:

  • 没有关键字=按值传递(IN)
  • ‘out’关键字=通过引用传递(REF),在呼叫之前没有明确的分配要求
  • ‘ref’Keyword =在调用之前通过引用(REF)具有明确的分配要求

C#没有真正的OUT或IN-OUT参数能力。

要查看C#中的’out’参数不是真正的OUT参数,可以使用以下代码:

  public class Test { Action _showValue; public void Run() { string local = "Initial"; _showValue = () => { Console.WriteLine(local.ToString()); }; Console.WriteLine("Passing by value"); inMethod(local); Console.WriteLine("Passing by reference with 'out' keyword"); outMethod(out local); Console.WriteLine("Passing by reference with 'ref' keyword"); refMethod(ref local); } void inMethod(string arg) { _showValue(); arg = "IN"; _showValue(); } void outMethod(out string arg) { _showValue(); arg = "OUT"; _showValue(); } void refMethod(ref string arg) { _showValue(); arg = "REF"; _showValue(); } } 

输出是:

 Passing by value Initial Initial Passing by reference with 'out' keyword Initial OUT Passing by reference with 'ref' keyword OUT REF 

正如您所看到的,’out’和’ref’实际上都通过了REF。 唯一的区别在于编译器如何将它们视为明确的赋值目的。

如果您有一个需要返回多个值的方法,则使用OUT关键字非常有用。 例如,查看int.TryParse()等方法。

使用REF更多的是对象的显式性。 请记住,传入方法的任何非原语都是通过引用传递的,在普通的托管代码中并不需要它。 通过声明REF关键字,您声明参数可能会在方法体中被修改,因此调用代码应该知道它(因此您也必须在调用代码中显式添加ref

如果您了解c ++,这可能会对您有所帮助:

 void Foo(Bar) {} // pass by value c# (if it's a value type ;)) void Foo(Bar) {} // c++ void Foo(Bar) {} // pass by reference c# (if it's a reference type ;)) void Foo(Bar&) {} // c++ void Foo(ref Bar) {} // c# void Foo(Bar*) // c++ void Foo(out Bar) {} // c# void Foo(Bar**) {} // c++ (the contents of *Bar needs to be filled up) 

你通过ref传递你想要被另一个函数读取和写入的东西,所以你应该传递初始化的变量才能被正确读取。

编辑:示例:

 void mymethod(ref int a) { a++; } 

你传递的东西是你想要由另一个函数写的东西,但你不需要初始化它,因为它不会被函数读取,只能写入。

编辑:示例:

 void mymethod2(out string a) { a="hello"; }