簡體   English   中英

在C#中使用ref參數的堆棧會發生什么?

[英]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;
}

在此, xy本質上是相同的變量-它們指向相同的存儲位置。 通過y可以看到對x所做的更改,反之亦然。 (請注意,在這種情況下,它是調用方中的局部變量,但不必如此-如果您通過引用傳遞了實例變量,則Bar可能會調用另一個方法來更改相同的變量,然后y將被視為“神奇地”改變...)

有關在C#中傳遞參數的更多信息,請參閱有關該主題的文章

通過引用意味着您可以更改傳遞給該項目的原始變量。 它基本上傳遞變量在堆棧上的地址,而不是變量值。

IL轉儲:

正如您實際詢問的那樣,堆棧上實際發生的事情是一個按引用按值方法的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM