2013-11-14 48 views
1

我不明白有關將參數傳遞給c#中的方法的一件事。從我看到的c#中的對象有時表現得像通過引用傳遞一次,就好像它們是按值傳遞一樣。在這段代碼中,我通過引用和通過值傳遞給method()。這兩個按預期執行。但是當我創建Update()並通過值傳遞一個對象時,我發現它的行爲就像更新原始對象一樣。爲什麼c#對象有一次像通過值傳遞和一次通過引用傳遞?

爲什麼我更新原始對象與Update(myString input)但不method(myString input)更新呢?

這是不符合邏輯!

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace ClassPassing 
{ 
class Program 
{ 
    static void Main(string[] args) 
    { 
     myString zmienna = new myString(); 

     Update(zmienna); 
     Console.WriteLine(zmienna.stringValue); 
     Console.WriteLine(zmienna.stringValue2); 
     Console.ReadLine(); 

     zmienna.stringValue = "This has run in main"; 
     zmienna.stringValue2 = "This is a help string"; 

     method(zmienna); 
     Console.WriteLine(zmienna.stringValue); 
     Console.WriteLine(zmienna.stringValue2); 
     Console.ReadLine(); 

     method(ref zmienna); 
     Console.WriteLine(zmienna.stringValue); 
     Console.WriteLine(zmienna.stringValue2); 

     Console.ReadLine(); 
    } 

    static void method(myString input) 
    { 
     input = new myString(); 
    } 

    static void method(ref myString input) 
    { 
     input = new myString(); 
    } 

    static void Update(myString input) 
    { 
     input.stringValue2 = "This has run in update method"; 
    } 
} 

public class myString 
{ 
    public string stringValue { get; set; } 
    public string stringValue2 { get; set; } 

    public myString() { stringValue = "This has been just constructed"; this.stringValue2 = "This has been just constructed"; } 
} 

}`

+0

您正在通過*引用*值,所以你有兩個引用都指向相同的類實例 – BrokenGlass

回答

4

你必須瞭解你的代碼:

static void method(myString input) 
{ 
    input = new myString(); 
} 

在這裏,您通過參考的價值,反對

static void method(ref myString input) 
{ 
    input = new myString(); 
} 

在這裏,您傳遞參考參考文獻

static void Update(myString input) 
{ 
    input.stringValue2 = "This has run in update method"; 
} 

這裏再次傳遞參考的價值

反對現在:

  1. 當你按值傳遞對象引用,你可以改變對象的內容,但您不能更改引用本身(將其分配給另一個對象)。
  2. 當通過參考通對象的引用,則可以既改變對象的內容,並可以修改基準本身(其分配給另一個對象)。

    class Program 
    { 
        public struct MyStruct 
        { 
         public int i; 
        } 
    
        public class MyClass 
        { 
         public int i; 
        } 
    
        public static void Modify(MyStruct s) 
        { 
         s.i = 99; 
        } 
    
        public static void Modify(MyClass c) 
        { 
         c.i = 99; 
        } 
    
        public static void Main(string[] args) 
        { 
         MyStruct myStruct = new MyStruct(); 
         myStruct.i = 20; 
         MyClass myClass = new MyClass(); 
         myClass.i = 20; 
    
         Modify(myStruct); 
         Modify(myClass); 
    
         Console.WriteLine("MyStruct.i = {0}", myStruct.i); 
         Console.WriteLine("MyClass.i = {0}", myClass.i); 
    
         Console.ReadKey(); 
        } 
    } 
    

    結果::

    MyStruct.i = 20 
    MyClass.i = 99 
    

真實由值在C#只通過簡單的(整數,浮點等等)類型的情況下和在struct案發生

在這種情況下,MyStruct的值保持不變,因爲它通過值傳遞給函數。在另一方面,MyClass的實例是通過引用傳遞這就是爲什麼它的價值變化。

+2

不是一個類的引用對一個對象的引用。再一次,區分類型和實例很重要。 –

+0

@JonSkeet你說得對,特別是OP想要了解C#中的引用是如何工作的。我已經更正了答案,現在應該可以。 – Spook

+0

謝謝,我現在明白了。 – Kruczkowski

4

對象根本不被通過。

對於引用類型的表達式(類,接口等)引用傳遞 - 通過值默認,但如果你使用ref的變量的引用傳遞。

重要的是要明白zmienna的值不是一個對象 - 它是一個參考。一旦你完成了排序,剩下的就變得簡單了。這不僅僅是參數傳遞 - 它的的一切。例如:

StringBuilder x = new StringBuilder(); 
StringBuilder y = x; 
y.Append("Foo"); 
Console.WriteLine(x); // Prints Foo 

這裏xy的值是同一個對象的引用 - 就像具有兩張紙,其中的每一個具有在相同的街道地址。所以如果有人通過閱讀寫在x上的地址來訪問房屋,並將前門塗成紅色,那麼其他人通過閱讀y上的地址訪問同一房屋,那第二個人也將看到紅色的前門。

有關更多詳細信息,請參閱我的文章reference and value typesparameter passing

0

可能存在多個問題,在這裏回答,但關於你的最後一個:

"Why do I update original object with Update(myString input) but do not update it with method(myString input)?"

在這裏,你所創建的myString類的新實例,而不是引用傳遞給了原方法作爲參數。因此,如果您在該方法內更改input.stringValue2的值,則在離開該方法後將丟失該值。

static void method(myString input) 
{ 
    input = new myString(); 
} 

但是,在這裏您引用了傳遞給它的原始實例。當您離開此方法時,原始myString實例將保留值stringValue2

static void Update(myString input) 
{ 
    input.stringValue2 = "This has run in update method"; 
} 
0

將計算機內存設想爲一組盒子,並且您可以使用標籤爲它們命名。

myString zmienna = new myString(); 

在這裏,您分配一個方塊,在它myString一個實例,並有一個標籤zmienna指向它。然後:

static void method(myString input) 
{ 
    input = new myString(); 
} 

在這種方法中,input是另一個標籤。調用您首先使標籤input指向與初始實例相同的方框的方法。但是在方法的正文中,您分配另一個框,並將標籤input更改爲指向該新框。第一個框沒有任何內容正在完成,並且zmienna標籤沒有任何內容正在完成。

static void method(ref myString input) 
{ 
    input = new myString(); 
} 

這裏,因爲ref關鍵字,你不再是隻透過第一「記憶盒子」的下落,但你給實際的標籤。因此,此方法的主體將您的標籤zmienna更新爲指向新創建的框,第二個實例爲myString。第一個盒子被遺忘了,因爲沒有標籤指向它。

static void Update(myString input) 
{ 
    input.stringValue2 = "This has run in update method"; 
} 

在這種情況下,您將傳遞第一個方框的地址,方法與第一個方法完全相同。所以你有兩個實驗室:zmiennainput - 都指向同一個盒子。因此input.stringValue2在與zmienna指向的相同框中訪問字段stringValue2

實際使用的準確的術語是參考,而不是我用了這樣的解釋標籤項。我以某種方式發現,很多人發現更容易理解這種方式:)