簡體   English   中英

在C#函數參數中提及ref

[英]Mentioning of ref in C# function parameters

我做了一個函數,我想像其中那樣傳遞字符串數組,例如string [] aa = null
在函數定義中,我對其進行了一些更新,然后發現在執行/返回函數時,其值未更新。我無法理解在C#中需要提到ref關鍵字的哪些內容。 它不是總是通過引用傳遞參數嗎,為什么需要提及ref? 為了通過引用傳遞,需要用ref提及哪些對象?

代碼就是這樣,只是試圖顯示我在做什么,如果不使用ref,我將無法更新值。

string[] temp = null 
foo(ref temp); 
//function definition
void foo (ref string[] temp) 
{
temp = {"Hello World ","You must be updated now"}
}
foreach(string s in temp)
System.Console.WriteLine(s)

您錯綜復雜。 如果傳遞參考對象,則它按值傳遞參考(請繼續閱讀,希望它應該開始變得更有意義)。 但是,這並不意味着它已通過引用。 實際上,默認情況下按值傳遞C#參數。

這是一篇很好的文章,很好地解釋了這一點

而且,這是一段片段,它解釋了我說您通過值傳遞引用時的意思。

在C#中,參數(默認情況下)按值傳遞,這意味着它們在傳遞給方法時會隱式復制。 對於值類型參數,這意味着物理復制實例(以復制p2的方式),而對於引用類型,這意味着復制參考 (以復制f2的方式)

但是,在這種情況下,您要傳入對象,然后在您的方法中創建一個具有新引用的新對象。 因此,原始參考保持不變,您可以通過執行更新而不是全新創建來查看。 當您明確說出ref時,您現在將引用傳遞給該引用,因此它起作用,因為您僅使用指向該引用的指針,並且在創建新對象時會將其放置在該引用位置。

正如eouw0o83hf所提到的,如果要創建一個全新的對象,則應使用out來表示這一點。 ref通常用於不通過引用傳遞的值對象。

總結一下:

  • 如果它是一個值類型,並且您希望更新該值並使它反映到各處,那么您需要使用ref
  • 如果是引用類型
    • 如果您希望更新該值以使其在任何地方都能反映出來,則可以正常傳入(無refout
    • 如果你想在方法內部創建一個全新的實例,並有反映,那么你應該使用out

更新

這是MSDN文章,確切解釋了您的要求:)

C#始終按值傳遞參數,除非您使用refout修飾符。

string[] temp = { "zero", "one", "two" };
MutateByVal(temp);
MutateByRef(ref temp);    

void MutateByVal(string[] arr)
{
    // arr and temp are separate references to the same array
    // the value of arr (a reference) is a copy of the value of temp

    // mutate the array referenced by arr
    arr[1] = "mutated!";
    // arr and temp still point at the same array
    // so both arr and temp now contain { "zero", "mutated!", "two" }

    // re-assign arr
    arr = new[] { "blah", "blah", "blah" };
    // arr and temp now point at different arrays
    // arr now contains { "blah", "blah", "blah" }
    // temp still contains { "zero", "mutated!", "two" }
}

void MutateByRef(ref string[] arr)
{
    // arr is an alias for temp
    // that is, they are two different names for the same reference

    // mutate the array referenced by arr
    arr[1] = "mutated!";
    // arr and temp are the same reference
    // so both arr and temp now contain { "zero", "mutated!", "two" }

    // re-assign arr
    arr = new[] { "blah", "blah", "blah" };
    // arr and temp are the same reference
    // so both arr and temp now contain { "blah", "blah", "blah" }
}

這是因為您實際上是在返回一個全新的對象,而不是修改現有對象上的條目。 如果要分配新對象,則應使用out

這是顯示outref和常規傳遞如何在數組arg上工作的示例。 如您所見,無論是否指定ref ,數組都是通過引用傳遞的; 但是,如果返回一個全新的對象,則需要指定out

class Program
{
    static void Main(string[] args)
    {
        string[] val;
        foo(out val);
        Console.WriteLine(string.Join(",", val));
        // Output: 1, 2

        bar(ref val);
        Console.WriteLine(string.Join(",", val));
        // Output: modified, 2

        bar2(val);
        Console.WriteLine(string.Join(",", val));
        // Output: modified again, 2

        Console.Read();
    }

    static void foo(out string[] temp)
    {
        temp = new string[]{"1", "2"};
    }

    static void bar(ref string[] temp)
    {
        temp[0] = "modified";
    }

    static void bar2(string[] temp)
    {
        temp[0] = "modified again";
    }
}

如果沒有ref ,對數組引用將被簡單地復制並傳遞給方法(按值-因為引用類型的是對對象的引用); 允許該方法訪問相同的陣列,但不允許它修改調用者的自己的參考。

這就是ref (或實際上是out )關鍵字的作用。

但是,如果您只希望方法更改引用對象,則不需要使用ref (取決於構造后是否可以更改類型,即,是否不可變)。

因此,此方法將覆蓋傳遞的數組的第一個元素:

public static void foo(string[] ss)
{
  if(ss!=null && ss.Length > 0)
    ss[0] = "Overwritten";
}

public static void main()
{
   var strings = new[] { String.Empty, "Original" };
   foo(strings);
   Console.WriteLine(strings[0]); //will print 'Overwritten'.
}

但是,在上面的代碼中foo無法完成的工作是ss參數的new ,並且期望它可以更改作為參數傳遞自main 的引用 strings

為此,我們需要傳遞本地strings引用 ,這是ref傳入的地方。如果這樣做,則可以更改main()中的strings main()如果傳遞null)以指向新數組:

public static void foo(ref string[] ss)
{
  if(ss==null || ss.Length == 0)
    ss= new string[1];

  ss[0] = "Overwritten";
}

public static void main()
{
   string[] strings = null
   foo(ref strings);
   Console.WriteLine(strings[0]); //will still print 'Overwritten'.
}

在C#中作為參數傳遞任何類型均由value傳遞。 因此,如果這是一個字符串數組,C#會創建引用的副本(這是引用類型)並傳遞給它。但是由於這是引用類型,所以兩個變量(來自內部方法范圍和外部參數)都將指向托管堆(或大對象堆)中的同一對象

代碼的問題是您創建了一個新變量(語法錯誤)。 要解決此問題,您只需直接使用數組的索引器即可,即:

void foo (string[] temp) // create a copy of a reference to the string array
{
    temp[0] = "Boom"; // temp still points to the same object
}
-------------
string[] temp = new [] {"one", "two", "three"}; //outer variable
foo(temp); // behind the scene we have two variables pointing to the same array
foreach (string s in temp)
    System.Console.WriteLine(s);

將類引用視為“對象ID”。 如果一個具有可變MyCar類類型的Car ,該變量不實際持有汽車。 相反,它保留實際存儲在其他位置的汽車的“對象ID”。 如果MyCar持有“#1543”,則語句MyCar.Color = CarColors.Purple; 實際上不會修改變量MyCar 相反,它將告訴系統“對象#1543應該是一輛Car 。將其Color屬性設置為CarColors.Purple." In many cases, a routine which passes a variable of type CarColors.Purple." In many cases, a routine which passes a variable of type Car CarColors.Purple." In many cases, a routine which passes a variable of type will simply want the called code to do something with the identified by that variable. In a few cases, however, one may be necessary to let the called code change the object ID stored within will simply want the called code to do something with the Car identified by that variable. In a few cases, however, one may be necessary to let the called code change the object ID stored within identified by that variable. In a few cases, however, one may be necessary to let the called code change the object ID stored within MyCar itself, so that it points to an entirely different instance of identified by that variable. In a few cases, however, one may be necessary to let the called code change the object ID stored within itself, so that it points to an entirely different instance of Car itself, so that it points to an entirely different instance of

在您的特定情況下,所討論的對象是一個數組。 被調用的例程創建了一個全新的數組,但是調用者以temp等於null開始。 調用者看到新數組的唯一方法是將對它的引用存儲到temp 否則,調用者將繼續查看之前的任何數組temp (如果未在其中存儲任何數組引用,則返回null )。

首先,您應該使用偽代碼。 但是,在此之前,這里有三件事:值類型,引用類型和“ ref”關鍵字。

值類型通常是簡單的基本類型,例如int,double等。string是一個怪異的類型,因為它被視為值類型。

引用類型是更復雜的類型,例如數組和類。

當您傳遞諸如int和double的值類型時,您將傳遞該值的副本,因此,如果將int x = 10傳遞給方法,則一旦離開該方法,該方法對x的更改將不會反映出來。 在另一方面,如果你通過MyClass的類class1,到Class 的屬性的任何更改將在函數外部的反射。 只是不要嘗試在您的方法內部新建一個class1,因為在調用者外部不會改變。

如果要在方法內更改值類型,請通過ref傳遞。 如果要新建一個新的類或數組,則可以通過ref傳遞。

還有一件事:使用out與ref之間不是那么黑白。 僅當該方法的設計總是僅在該方法內部創建您的類或數組時,您才會用光。 如果要允許創建新對象的可能性,可以對引用類型使用ref。 喜歡,

    //function definition 
    void foo (ref string[] temp)  
    {
        if(temp == null)
        {
            temp = new string[] { "Hello World ", "You must be updated now" };
        }
        else
        { 
             // do something with the existing temp
        }
    } 

最后,如果這是您的實際代碼:

        string[] temp = null;
        foo(ref temp);

        foreach (string s in temp)
            System.Console.WriteLine(s);

后來:

    //function definition 
    void foo (ref string[] temp)  
    {
        temp = new string[] { "Hello World ", "You must be updated now" };
    } 

然后,它應該可以正常工作。

暫無
暫無

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

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