2012-10-12 89 views
0

如果在.NET中創建類,它將是引用類型。在這個例子中,我在SomeMethod中設置了MyClass的名字。現在在Main方法中,我不需要從SomeMethod中分配返回值,因爲傳入的參數是引用類型。我想在主要方法中的實例具有新的更新名稱「約翰」,它的確如此。將返回值分配給.NET中的引用類型

爲了便於閱讀,我經常爲受讓人返回返回值,即使這不是必要的,以便將更新後的屬性重新分配給main中的原始實例。我的假設是編譯器足夠聰明,不會在Main方法內重新創建MyClass的新引用。真的嗎?或者編譯器實際上是否創建了一個新的引用,並在爲自己分配返回值時指向更新後的值?

//My class will be a reference type 
public class MyClass 
{ 
    public int ID { get; set; } 
    public string name { get; set; } 
} 

public class Main 
{ 
    //Main method of application 
    public void Main() 
    { 
     MyClass myClass = new MyClass(); 
     //return value back to itself 
     myClass = SomeMethod(myClass); 
    } 

    public MyClass SomeMethod(MyClass myClass) 
    { 
     myClass.name = "John"; 
     return myClass; 
    } 
} 

這裏是IL

instance void Main1() cil managed 
{ 
    // Method begins at RVA 0x207c 
    // Code size 15 (0xf) 
    .maxstack 2 
    .locals init (
     [0] class Test.MyClass myClass 
    ) 

    IL_0000: newobj instance void Test.MyClass::.ctor() 
    IL_0005: stloc.0 
    IL_0006: ldarg.0 
    IL_0007: ldloc.0 
    IL_0008: call instance class Test.MyClass Test.Main::SomeMethod(class Test.MyClass) 
    IL_000d: stloc.0 
    IL_000e: ret 
} // end of method Main::Main1 

回答

1

要回答你的問題,是的編譯器足夠聰明,不會將返回值重新分配給同一個對象。如果我們打開與反射器或dotPeek,你的代碼代碼:

public void Main() 
{ 
    MyClass myClass = new MyClass(); 
    //return value back to itself 
    myClass = SomeMethod(myClass); 
} 

僅僅是變換到:

public void Main() 
{ 
    this.SomeMethod(new MyClass()); 
} 

即使我們做了重新分配變量的東西,它仍然是由編譯器中刪除。例如,編譯器轉換這樣的:

public void Main() 
{ 
    MyClass myClass = new MyClass(); 
    //return value back to itself 
    myClass = SomeMethod(myClass); 

    Console.WriteLine(myClass.name); 
} 

到:

public void Main() 
{ 
    Console.WriteLine(this.SomeMethod(new MyClass()).name); 
} 

但是,像L CWS個Therin和羅伯特·傑普森說,你不應該這樣做,因爲這是不好的做法,並可能導致誤解

+0

你確定它變成了「this.SomeMethod」嗎?考慮到Main是一種靜態方法?雖然我注意到你省略了「靜態」關鍵字(在這種情況下,你的答案可能是正確的)。 –

+0

你是對的,我創建了主類。但編譯器使用靜態方法(沒有「this」)做同樣的事情。而OP也沒有靜態方法 –

+0

是的,看起來你是正確的。我在原帖中添加了IL。 – Thomas

1

類被作爲參考方法過去了,所以你要修改的同樣的事情,你回來了。

當一個基於引用類型的對象被傳遞給一個方法時,沒有 該對象的副本被創建。相反,對作爲方法參數的對象 的引用被創建並傳遞。因此通過 所做的更改將在調用方法中反映出來。

來源:http://msdn.microsoft.com/en-us/library/ms173114(v=vs.80).aspx

關於可讀性:你的方法應該永遠是不言自明的。在我看來,方法的名稱應該說明它的作用。

void AddClientContextToContainer(Container destination) 
+0

我想你錯過了我的問題。我明白這個類本身沒有通過,它是我提到的一個引用類型。我問是否在主方法中通過賦值返回值,編譯器是否在主方法中創建了另一個「引用」。 – Thomas

+0

'類是通過引用方法傳遞的。不,他們不是。類*是*引用,它們通過* value *(除非使用ref/out)傳遞給方法。被複制的值只是一個參考。 – Servy

+0

@Servy你說得對。他們通過**作爲參考,而不是通過參考。 –

2

它不創建新的參考。

但是,它沒有任何意義。這應該足夠了:

public void SomeMethod(MyClass myClass) 
{ 
    myClass.name = "John"; 

} 

以上是自然的,實際上比您建議的方式更不復雜。

+0

我瞭解它不需要。正如我所說,它似乎更易於我閱讀。 – Thomas

+0

真的嗎?我不同意。我想每個人都有自己的想法。 –

+5

@Thomas返回修改後的實例向消費者指出這是一個新實例,並且傳入的實例將不會受到傷害,顯然不是這種情況。有害! –

1

在你的情況下,你正在創建一個實例的對象。實際實例通過new創建。參考本質上只是一個整數(32位系統上的32位,64位系統上的64位)。該整數指向對象實例在內存中的位置。 「創建一個新的參考」本質上與創建一個新整數的工作量相同(意思是非常非常小;它是關於計算機可以執行的最小可能工作單位)。

void替代方案相反,您的代碼將執行兩個(冗餘)引用副本。 (一次將方法參數複製到堆棧中的返回值,然後再從該返回值返回到原始變量。)

實際的額外工作量很小。它存在,但它確實很小。這裏的關注點應該只是可讀性。如果你發現代碼更易於閱讀和理解,那麼很好。請注意,這是一種通常只在MyClass是不可變的情況下使用的模式,其中返回值是對新實例的引用,而不是傳入的實例。這可能會導致其他人閱讀您的代碼時感到困惑。如果他們錯誤地認爲該對象是不可變的,它可能會導致......不好的事情。

其他人建議使該方法返回void,但我會說,它可能對類變得不可變是有意義的。你已經遵循了一個更加不可改變的模式。

+0

這是我關心的問題。是否在主要方法中創建了另一個參考,您聲稱它的確存在(您是否有參考,沒有雙關語意思)。乍一看,這似乎是自然而然的正確答案。我想知道編譯器是否足夠聰明,知道它不需要在這裏創建另一個引用,因爲在技術上沒有必要。 – Thomas

+0

@Thomas將「參考」與「實例」區分開來非常重要。您正在執行「引用」的副本,但您並未創建或複製任何實際的「實例」。複製引用與複製單個整數相同;它是相同數量的工作(正如我所說的那樣,無論它所引用的對象的大小如何,該數量都是**小**)。 – Servy

相關問題