前幾天有人問我應該使用參數關鍵字out
而不是ref
。 雖然我(我認爲)理解了ref
和out
關鍵字之間的差別( 以前已經提到過 ),最好的解釋彷佛是ref
== in
and out
,什麼是一些(假設的或代碼的)例子我應該老是out
而不是ref
。 服務器
因爲ref
更爲通用,爲何你想out
? 它只是語法糖嗎? less
它取決於編譯上下文(參見下面的示例)。 函數
out
和ref
都表示經過引用傳遞的變量,但ref
要求變量在傳遞以前被初始化,這多是Marshaling上下文中的一個重要區別(Interop:UmanagedToManagedTransition,反之亦然) ui
MSDN警告 : spa
Do not confuse the concept of passing by reference with the concept of reference types. The two concepts are not the same. A method parameter can be modified by ref regardless of whether it is a value type or a reference type. There is no boxing of a value type when it is passed by reference.
指針
來自官方MSDN文檔: code
out
: The out keyword causes arguments to be passed by reference. This is similar to the ref keyword, except that ref requires that the variable be initialized before being passed
對象
ref
: The ref keyword causes an argument to be passed by reference, not by value. The effect of passing by reference is that any change to the parameter in the method is reflected in the underlying argument variable in the calling method. The value of a reference parameter is always the same as the value of the underlying argument variable.
ip
在賦值參數時,咱們能夠驗證out和ref是否確實相同: 內存
CIL示例 :
請考慮如下示例
static class outRefTest{ public static int myfunc(int x){x=0; return x; } public static void myfuncOut(out int x){x=0;} public static void myfuncRef(ref int x){x=0;} public static void myfuncRefEmpty(ref int x){} // Define other methods and classes here }
在CIL中, myfuncOut
和myfuncRef
的指令與預期的徹底相同。
outRefTest.myfunc: IL_0000: nop IL_0001: ldc.i4.0 IL_0002: starg.s 00 IL_0004: ldarg.0 IL_0005: stloc.0 IL_0006: br.s IL_0008 IL_0008: ldloc.0 IL_0009: ret outRefTest.myfuncOut: IL_0000: nop IL_0001: ldarg.0 IL_0002: ldc.i4.0 IL_0003: stind.i4 IL_0004: ret outRefTest.myfuncRef: IL_0000: nop IL_0001: ldarg.0 IL_0002: ldc.i4.0 IL_0003: stind.i4 IL_0004: ret outRefTest.myfuncRefEmpty: IL_0000: nop IL_0001: ret
nop :沒有操做, ldloc :load local, stloc :stack local, ldarg :load參數, bs.s :branch to target ....
(參見: CIL指令列表 )
使用out表示該參數未被使用,僅設置。 這有助於調用者瞭解您始終初始化參數。
此外,ref和out不只適用於值類型。 它們還容許您重置引用類型在方法中引用的對象。
out
是ref
更多約束版本。
在方法體中,您須要在離開方法以前分配全部out
參數。 一樣,將忽略分配給out
參數的值,而ref
要求分配它們。
因此out
容許你這樣作:
int a, b, c = foo(out a, out b);
其中ref
須要分配a和b。
你應該用out
除非你須要ref
。
當數據須要被編組到例如另外一個過程時,這會產生很大的不一樣,這多是昂貴的。 所以,當方法不使用它時,您但願避免編組初始值。
除此以外,它還向讀者顯示聲明或調用初始值是否相關(並可能保留)或丟棄。
做爲次要差別,不須要初始化out參數。
out
例子:
string a, b; person.GetBothNames(out a, out b);
其中GetBothNames是一種以原子方式檢索兩個值的方法,該方法不會改變行爲,不管a和b是什麼。 若是呼叫轉到夏威夷的服務器,將初始值從這裏複製到夏威夷是浪費帶寬。 使用ref的相似代碼片斷:
string a = String.Empty, b = String.Empty; person.GetBothNames(ref a, ref b);
可能會混淆讀者,由於它看起來像a和b的初始值是相關的(雖然方法名稱代表它們不是)。
ref
示例:
string name = textbox.Text; bool didModify = validator.SuggestValidName(ref name);
這裏初始值與方法相關。
你是正確的,在語義上, ref
提供「in」和「out」功能,而out
只提供「out」功能。 有一些事情須要考慮:
out
要求接受參數的方法必須在返回以前的某個時刻爲變量賦值。 您能夠在某些鍵/值數據存儲類中找到此模式,例如Dictionary<K,V>
,其中包含TryGetValue
等函數。 此函數採用out
參數來保存檢索時的值。 這是沒有意義的來電者傳遞一個值到這個功能,因此out
是用來保證必定的價值會在通話結束後的變量,即便它是否是「真正」的數據(在的狀況下,不存在密鑰的TryGetValue
)。 out
和ref
參數的編組方式不一樣 另外,值得注意的是,雖然引用類型和值類型的值的性質不一樣,但應用程序中的每一個變量都指向一個包含值的內存位置 ,即便對於引用類型也是如此。 只是在引用類型中,內存位置中包含的值是另外一個內存位置。 將值傳遞給函數(或執行任何其餘變量賦值)時,該變量的值將複製到另外一個變量中。 對於值類型,這意味着將複製該類型的整個內容。 對於引用類型,這意味着將複製內存位置。 不管哪一種方式,它確實建立了變量中包含的數據的副本。 惟一真正的相關性涉及賦值語義; 當分配變量或傳遞值(默認值)時,當對原始(或新)變量進行新賦值時,它不會影響另外一個變量。 在引用類型的狀況下,是的,雙方均可以對實例進行更改,但這是由於實際變量只是指向另外一個內存位置的指針; 變量的內容 - 內存位置 - 實際上沒有改變。
使用ref
關鍵字傳遞說原始變量和函數參數實際上都指向相同的內存位置。 這再次隻影響賦值語義。 若是爲其中一個變量分配了新值,那麼由於另外一個指向同一個內存位置,新值將反映在另外一側。