簡體   English   中英

C#ref關鍵字用法

[英]C# ref keyword usage

我的理解(或至少我相信我做的),這意味着什么路過一個類的方法的一個實例ref對不及格的ref 何時或在什么情況下應該通過ref傳遞類實例? 在為類實例使用ref關鍵字時是否有最佳實踐?

我輸出和參數參數的最清楚的解釋是...... Jon Skeet。

參數在C#中傳遞

他沒有進入“最佳實踐”,但如果你理解他給出的例子,你就會知道什么時候需要使用它們。

如果您可以替換原始對象,則應將其作為ref發送給他。 如果它只是用於輸出並且在調用函數之前可以未初始化 ,那么你將out

簡而言之,如果您希望調用的函數能夠更改該變量的值,則可以將值作為ref參數傳遞。

這與將引用類型作為參數傳遞給函數不同。 在這些情況下,您仍然按值傳遞,但該值參考。 在傳遞ref的情況下,然后發送對變量的實際引用; 實質上,你和你正在調用的函數“共享”相同的變量。

考慮以下:

public void Foo(ref int bar)
{
    bar = 5;
}

...

int baz = 2;

Foo(ref baz);

在這種情況下, baz變量的值為5,因為它是通過引用傳遞的。 語義對於值類型是完全清楚的,但對於引用類型不是很清楚。

public class MyClass
{
    public int PropName { get; set; }
}

public void Foo(MyClass bar)
{
    bar.PropName = 5;
}

...

MyClass baz = new MyClass();

baz.PropName = 2;

Foo(baz);

正如預期的那樣, baz.PropName將為5,因為MyClass是一個引用類型。 但是,我們這樣做:

public void Foo(MyClass bar)
{
    bar = new MyClass();

    bar.PropName = 5;
}

使用相同的調用代碼, baz.PropName將保持為2.這是因為即使MyClass是引用類型, Foo也有自己的bar變量; barbaz剛開始時的值相同,但一旦Foo分配了一個新值,它們只是兩個不同的變量。 但是,如果我們這樣做:

public void Foo(ref MyClass bar)
{
    bar = new MyClass();

    bar.PropName = 5;
}

...

MyClass baz = new MyClass();

baz.PropName = 2;

Foo(ref baz);

我們最終將PropName 5,因為我們通過引用傳遞了baz ,使得兩個函數“共享”相同的變量。

ref關鍵字允許您通過引用傳遞參數。 對於引用類型,這意味着傳遞對象的實際引用(而不是該引用的副本)。 對於值類型,這意味着傳遞對包含該類型值的變量的引用。

這用於需要返回多個結果但不返回復雜類型以封裝這些結果的方法。 它允許您將對象的引用傳遞給方法,以便該方法可以修改該對象。

要記住的重要一點是引用類型通常不通過引用傳遞,傳遞引用的副本。 這意味着您沒有使用傳遞給您的實際引用。 當您在類實例上使用ref ,您將傳遞實際引用本身,因此對它的所有修改(例如將其設置為null )將應用於原始引用。

我發現使用ref關鍵字很容易遇到麻煩。

即使沒有方法簽名中的ref關鍵字,以下方法也會修改f,因為f是引用類型:

public void TrySet(Foo f,string s)
{
   f.Bar = s;
}

然而,在第二種情況下,原始Foo僅受第一行代碼的影響,方法的其余部分以某種方式創建並僅影響新的局部變量。

public void TryNew(Foo f, string s)
{
  f.Bar = ""; //original f is modified
  f = new Foo(); //new f is created
  f.Bar = s; //new f is modified, no effect on original f
}

如果編譯器在這種情況下給你一個警告會很好。 基本上你正在做的是將你收到的引用替換為另一個引用不同內存區域的引用。

您實際上想要用新實例替換該對象,請使用ref關鍵字:

public void TryNew(ref Foo f, string s)...

但是你不是在腳下射擊嗎? 如果調用者不知道創建了新對象,則以下代碼可能無法按預期工作:

Foo f = SomeClass.AFoo;
TryNew(ref f, "some string"); //this will clear SomeClass.AFoo.Bar and then create a new distinct object

如果您嘗試通過添加行來“修復”問題:

SomeClass.AFoo = f;

如果代碼在其他地方擁有對SomeClass.AFoo的引用,那么該引用將變為無效...

作為一般規則,您可能應該避免使用new關鍵字來更改從另一個類讀取的對象或作為方法中的參數接收的對象。

關於ref關鍵字與引用類型的使用,我可以建議這種方法:

1)如果只是設置引用類型的值但是在函數或參數名稱以及注釋中是明確的,請不要使用它:

public void SetFoo(Foo fooToSet, string s)
{
  fooToSet.Bar = s;
}

2)如果有合理的理由用新的不同實例替換輸入參數,請使用帶有返回值的函數:

public Foo TryNew(string s)
{
  Foo f = new Foo();
  f.Bar = s;
  return f;
}

但使用此函數可能仍會對SomeClass.AFoo場景產生不良后果:

SomeClass.AFoo = TryNew("some string");//stores a different object in SomeClass.AFoo 

3)在某些情況下,如串交換例子這里是得心應手REF參數,可以只是在情況2確保交換的對象地址不會影響您的代碼的其余部分。

因為它為你管理內存分配,所以C#很容易忘記內存管理的一切,但它確實有助於理解指針和引用的工作方式。 否則,您可能會引入難以發現的細微錯誤。

最后,通常情況下,人們會想要使用類似memcpy的函數,但我知道C#中沒有這樣的東西。

將引用類型(非值類型)傳遞給方法時,在兩種情況下都只傳遞引用。 但是當您使用ref關鍵字時,被調用的方法可以更改引用。

例如:

public void MyMethod(ref MyClass obj) 
{
   obj = new MyClass();
}

別處:

MyClass x = y; // y is an instance of MyClass

// x points to y

MyMethod(ref x);

// x points to a new instance of MyClass

在調用MyMethod(ref x)時 ,x將在方法調用后指向新創建的對象。 x不再指向原始對象。

通過引用傳遞引用變量的大多數用例涉及初始化和out比ref更合適。 並且它們編譯為相同的東西(編譯器強制執行不同的約束 - 在傳入之前初始化ref變量,並且在方法中初始化out變量)。 因此,我能想到的唯一一個有用的地方就是你需要檢查一個實例化的ref變量,並且可能需要在某些情況下重新初始化。

這可能也是修改Asaf R.指出的不可變類(如字符串)所必需的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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