2016-11-27 50 views
2

說你有一個像這樣爲什麼增加運營商可能對性能,同時裁判是不是

public class Foo 
{ 
    public int Bar { get; set; } = 42; 
} 

一類。如果你試圖將財產傳給作爲ref參數編譯器會發出錯誤

CS0206屬性或分度器可能不作爲輸出或參考 參數

這是可以理解的,因爲在普拉克在上面的例子中將屬性編號爲get_Bar()set_Bar()方法。但是,如果您在屬性上使用增量運算符,如

var foo = new Foo(); 
foo.Bar++; 

它按預期工作。爲了實現這一目標,編譯器需要產生這樣的僞代碼:

var foo = new Foo(); 
int tmp = foo.get_Bar(); 
tmp++; 
foo.set_Bar(tmp); 

所以理論上編譯器可以做類似的事情ref,如:

var foo = new Foo(); 
int tmp = foo.get_Bar(); 
DoSomething(ref tmp); 
foo.set_Bar(tmp); 

是否有技術原因編譯器不這麼做,或者這只是C#團隊的設計決定?

+2

這實際上是在VB.NET編譯器做出這樣的代碼編譯什麼。它是那種被認爲是*友善*的語言,C#本意是*純*。如果純度與隱藏可能很昂貴的setter調用不兼容,在錯誤的地方生成異常,可能會產生難以診斷的編譯錯誤或導致別名問題。 ++運算符不是非常純粹的,並且以不止一種方式避開規則。例如,您可以增加一個*字節*而不用強制轉換。他們決定讓它*實用*。選擇。 –

+0

可能的重複:http://stackoverflow.com/questions/1402803/passing-properties-by-reference-in-c-sharp – Abion47

+0

@HansPassant我會接受它,如果你可以詳細說明你的意見到一個答案 –

回答

2

就像HansPassant說的那樣,這是C#團隊在編寫C#規範時所做的設計決定,因此您必須問其中之一以獲得正確答案。

但是,如果我想冒個猜測,那麼編譯器魔法的數量會通過一個屬性ref將導致幕後發生足夠的不明顯的操作,從而導致解決方案不可取。例如,當前如何遞增/遞減屬性的工作方式如您所說:程序將屬性的支持字段的值分配給臨時變量,執行操作並將結果重新分配給屬性。這是一個簡單的過程,不包含任何困難的概念。

但是,爲了做到這一點,幕後魔法通過ref屬性,然而,過程變得更加涉及。當一個值類型被ref傳遞時,通過參數傳遞的實際值是一個指向值類型變量的指針。但是,爲了做到這一點,你必須做類似於第二個例子的事情。這會導致臨時變量的地址,而不是屬性本身傳遞給方法。這種行爲可能會導致一些無法預料的和難以理解的後果,試圖以某種方式操縱ref參數。

所以我猜測增量操作符很容易換行,因爲它只處理值,而ref關鍵字更復雜,因爲它也必須擔心範圍和內存地址。

編輯:對我來說,對於一個字段,被調用方法內部的任何操作都會反映在字段本身上的另一個原因。這些操作可以被其他線程看到,並且在方法執行期間訪問該字段(關於併發字段可訪問性的最佳實踐)。

但是,對於參數,在方法返回並將值複製回來之前,方法內部發生的任何更改都將不可見。這會導致字段和屬性之間的行爲不一致,其原因並不明顯。

(就個人而言,我認爲這是一個更可能的理由在性能不支持ref。)