簡體   English   中英

在c#中,當向方法發送參數時,我們何時應該使用“ref”和何時“out”以及什么時候不使用它們?

[英]In c# , when sending a parameter to a method, when should we use “ref” and when “out” and when without any of them?

在c#中,當向方法發送參數時,我們何時應該使用“ref”和何時“out”以及什么時候不使用它們?

一般來說,如果可能的話,你應該避免使用ref和out。

話雖這么說,當方法可能需要修改值時使用ref。 當方法總是應該為值賦值時使用out。

ref和out之間的區別在於,當使用時,編譯器會強制執行規則,您需要在返回之前為out參數指定一些內容。 使用ref時,必須在將變量用作ref參數之前為變量賦值。

顯然,以上適用於您編寫自己的方法時。 如果需要調用在參數上使用ref或out修飾符聲明的方法,則在調用方法時應在參數之前使用相同的修飾符。

還要記住,C#通過引用傳遞引用類型(類)(如,引用按值傳遞)。 因此,如果您提供一些引用類型作為參數的方法,該方法可以修改對象的數據; 即使沒有引用或退出。 但它不能修改引用本身(因為它不能修改引用的對象)。

它們主要用於從方法調用中獲取多個返回值。 就個人而言,我傾向於不使用它們。 如果我想從方法中獲得多個返回值,那么我將創建一個小類來保存它們。

當您想要從該參數中的方法返回某些內容時,將使用ref和out。 我記得,他們實際上都編譯成了同樣的IL,但C#實現了一些額外的東西,所以你必須具體。

這里有些例子:

static void Main(string[] args)
{
    string myString;
    MyMethod0(myString);
    Console.WriteLine(myString);

    Console.ReadLine();
}

public static void MyMethod0(string param1)
{
    param1 = "Hello";
}

以上將無法編譯,因為myString從未初始化。 如果myString初始化為string.Empty,則程序的輸出將為空行,因為所有MyMethod0都會將新字符串分配給對param1的本地引用。

static void Main(string[] args)
{
    string myString;
    MyMethod1(out myString);
    Console.WriteLine(myString);

    Console.ReadLine();
}


public static void MyMethod1(out string param1)
{
    param1 = "Hello";
}

myString未在Main方法中初始化,但程序輸出“Hello”。 這是因為Main方法中的myString引用正在從MyMethod1更新。 MyMethod1不希望param1已經包含任何內容,因此它可以保持未初始化狀態。 但是,該方法應該分配一些東西。

static void Main(string[] args)
{
    string myString;
    MyMethod2(ref myString);
    Console.WriteLine(myString);

    Console.ReadLine();
}

public static void MyMethod2(ref string param1)
{
    param1 = "Hello";
}

這再次不會編譯。 這是因為ref要求Main方法中的myString首先被初始化為某個東西。 但是,如果更改Main方法以便將myString初始化為string.Empty,則代碼將編譯並且輸出將為Hello。

因此,區別出來可以與未初始化的對象一起使用,ref必須傳遞初始化對象。 如果您傳遞的對象沒有任何引用,則無法替換它。

需要明確的是:如果傳遞的對象已經是引用類型,則該方法可以更新對象,並且更新將反映在調用代碼中,但是無法更改對對象的引用。 所以,如果我寫這樣的代碼:

static void Main(string[] args)
{
    string myString = "Hello";
    MyMethod0(myString);
    Console.WriteLine(myString);

    Console.ReadLine();
}

public static void MyMethod0(string param1)
{
    param1 = "World";
}

程序的輸出將是Hello,而不是World,因為該方法僅更改了引用的本地副本,而不是傳入的引用。

我希望這是有道理的。 我的一般經驗法則是不使用它們。 我覺得這是回到OO之前的日子。 (但那只是我的個人意見)

(這是現有答案的補充 - 一些額外的考慮因素)

在C#中使用ref還有另一種情況,更常見於XNA之類的東西......通常,當你傳遞一個值類型( struct )時,它會被克隆。 這使用堆棧空間和一些CPU周期,並且具有副作用,即在調用的方法中對struct任何修改都將丟失。

(除此之外:通常struct應該是不可變的,但是XNA中的可變結構並不罕見)

為了解決這個問題,在這些程序中看到ref很常見。

但是在大多數程序中(例如,您使用class es作為默認值), 通常可以通過“按值”傳遞引用(即沒有ref / out )。


另一種非常常見的用例out的是Try*模式,例如:

string s = Console.ReadLine();
int i;
if(int.TryParse(s, out i)) {
    Console.WriteLine("You entered a valid int: " + i);
}

或者類似地,在字典上使用TryGetValue

這可以使用元組代替,但它是一種常見的模式,它可以被合理地理解,即使是那些為過多的ref / out而苦苦掙扎的人。

真的很簡單。 您使用與方法中最初聲明參數的關鍵字完全相同的關鍵字。 如果它被聲明為out ,你必須out 如果它被聲明為ref ,則必須使用ref

除了Colin的詳細答案,您還可以使用out參數從一個方法調用中返回多個值。 例如,參見下面的方法,它返回3個值。

static void AssignSomeValues(out int first, out bool second, out string third)
    {
        first = 12 + 12;
        second = false;
        third = "Output parameters are okay";
    }

你可以像這樣使用它

static void Main(string[] args) {
        int i;
        string s;
        bool b;

        AssignSomeValues(out i, out b, out s);

        Console.WriteLine("Int value: {0}", i);
        Console.WriteLine("Bool value: {0}", b);
        Console.WriteLine("String value: {0}", s);

        //wait for enter key to terminate program
        Console.ReadLine(); }

只需確保為每個out參數指定一個有效值,以避免出錯。

盡量避免使用ref。 Out是可以的,因為你知道將會發生什么,舊值將消失,即使函數失敗,新值也將在你的變量中。 但是,只要查看函數,您就不知道ref參數會發生什么。 它可以是相同的,修改的或全新的對象。

每當我看到ref,我都會感到緊張。

ref是要避免的(我相信這也有一個fx-cop規則)但是當引用的對象本身可能改變時使用ref。 如果您看到'ref'關鍵字,則您知道在調用該方法后,基礎對象可能不再被同一變量引用。

暫無
暫無

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

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