使用REF和OUT关键字在C#中按值传递和传递值
这是我到目前为止所理解的:
通过价值
按值传递意味着传递参数的副本。 对该副本的更改不会更改原始副本。
通过参考
通过引用传递意味着传递对原始的引用。 对引用的更改会影响原始引用。
REF关键字
REF告诉编译器在进入函数之前初始化对象。 REF表示该值已经设置,因此该方法可以读取并修改它。 REF有两种方式,包括进出。
OUT关键字
OUT告诉编译器该对象将在函数内初始化。 OUT表示该值尚未设置,因此必须在调用return之前设置。 OUT只是一种方式,即出局。
题
那么在什么情况下你会结合使用ref和out关键字,通过引用传递还是通过值传递? 例子会有很大帮助。
非常感谢。
你永远不会在1参数上组合ref
和out
。 它们都意味着“通过引用传递”。
您当然可以在一种方法中组合ref参数和输出参数。
ref
和out
之间的区别主要在于意图 。 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 word
或StringBuilder 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"; }