[英]What happens on the stack with ref parameters in c#?
我正在閱讀一些有關WCF和IDispatchMessageInspector的C#文檔,該接口定義了一個“消息”對象,該對象通過引用傳遞,以便可以對其進行操作。
當您通過ref傳遞內容而不是正常傳遞內容時,堆棧上實際發生了什么?
引用傳遞的不是對象 ,而是變量 。
基本上,它將別名用作在調用方用作參數的變量,以及在您調用的方法中的參數的別名:
public void Foo()
{
int x = 10;
Bar(ref x);
Console.WriteLine(x); // Prints 20
}
public void Bar(ref int y)
{
y = 20;
}
在此, x
和y
本質上是相同的變量-它們指向相同的存儲位置。 通過y
可以看到對x
所做的更改,反之亦然。 (請注意,在這種情況下,它是調用方中的局部變量,但不必如此-如果您通過引用傳遞了實例變量,則Bar
可能會調用另一個方法來更改相同的變量,然后y
將被視為“神奇地”改變...)
有關在C#中傳遞參數的更多信息,請參閱有關該主題的文章 。
通過引用意味着您可以更改傳遞給該項目的原始變量。 它基本上傳遞變量在堆棧上的地址,而不是變量值。
正如您實際詢問的那樣,堆棧上實際發生的事情是一個按引用和按值方法的IL轉儲:
.method private hidebysig instance void ByRef(string& s) cil managed
{
// Code size 9 (0x9)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldstr "New value"
IL_0007: stind.ref
IL_0008: ret
} // end of method Class1::ByRef
與
.method private hidebysig instance void ByValue(string s) cil managed
{
// Code size 9 (0x9)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "New value"
IL_0006: starg.s s
IL_0008: ret
} // end of method Class1::ByValue
如您所見,主要區別在於參數類型( string&
而不是string
),並且它需要執行額外的步驟來間接加載和存儲值。
下面顯示了簡單的C#源供參考:
void ByRef(ref string s)
{
s = "New value";
}
void ByValue(string s)
{
s = "New value";
}
當您通過價值傳遞某些東西時; 會在調用函數之前創建值類型的副本並放在堆棧上。 當您通過引用傳遞某些內容時,該對象的地址將被壓入堆棧,而不是創建一個副本;當您在函數中修改該對象時,則會修改原始對象而不是副本。
它的工作方式是,當編譯器看到參數由ref傳遞時,會將您對變量的引用轉換為間接內存訪問。
例如,讓我們假設在存儲位置100處您有一個整數123;在存儲位置100處有一個整數。 現在,當您調用一個按值接受(默認值)的函數時,將制作123的副本並在調用該函數之前將其壓入堆棧,這意味着該副本現在位於(假設)160地址。 但是,當您通過引用傳遞某些內容時,地址100將被壓入堆棧,並將位於位置160。
現在,生成的指令將讀取160以獲取對象的位置,然后在100處修改數據。使用*運算符時,將發生與指針操作相同的間接操作。
通過ref表示您可以為傳遞的對象創建新的實例,通過值不能僅僅改變對象的屬性
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.