[英]How does C# evaluate expressions which contain assignments?
我有C / C ++背景。 我遇到了一種在C#中交換兩個值的奇怪方式。
int n1 = 10, n2=20;
n2 = n1 + (n1=n2)*0;
在C#中,上面兩行在n1
和n2
之間進行交換。 這對我來說是一個驚喜,因為在C / C ++中,結果應該是n1=n2=20
。
那么,C#如何評估表達式 ? 它看起來像上面的+
被視為一個function calling
我。 以下解釋似乎是可行的。 但似乎對我很熟悉。
(n1=n2)
。 因此n1=20
。 n1
n1+ (n1=n2)*0
中的n1+ (n1=n2)*0
還不是20。 它被視為一個函數參數,因此被推入堆棧並且仍為10.因此, n2=10+0=10
。 在C#中,子表達式按從左到右的順序進行計算,副作用按此順序生成。 這在C#5規范的第7.3節中定義:
表達式中的操作數從左到右進行計算。
重要的是要認識到子表達式評估的順序與優先級(也就是操作順序)和關聯性無關。 例如,在類似A() + B() * C()
的表達式中。 C#中的評估順序始終是A()
, B()
, C()
。 我對C / C ++的有限理解是這個順序是一個編譯器實現細節。
在您的示例中,首先計算n1(10)的左操作數。 然后評估(n1=n2)
。 其結果是n2(20)的值,產生分配給n1的副作用。 n1現在是20.然后20 * 0的乘法產生0.然后計算10 + 0並且將結果(10)分配給n2。 因此,最終的預期狀態是n1 = 20且n2 = 10。
好的,這可能最好用IL操作碼來解釋。
IL_0000: ldc.i4.s 0A
IL_0002: stloc.0 // n1
IL_0003: ldc.i4.s 14
IL_0005: stloc.1 // n2
前4行有點自我解釋ldc.i4加載變量(int大小為4)只有堆棧而stloc。*將值存儲在堆棧頂部
IL_0006: ldloc.0 // n1
IL_0007: ldloc.1 // n2
IL_0008: stloc.0 // n1
IL_0009: stloc.1 // n2
這些行基本上就是你所描述的。 每個值只加載堆棧,在n2之前加載n1然后存儲但是在n2之前存儲n1(因此交換)
我相信這是.NET規范中描述的正確行為。
mikez還添加了更多細節並幫助我找到答案,但我相信答案在7.3.1中得到了解釋
當操作數出現在具有相同優先級的兩個運算符之間時,運算符的關聯性控制執行操作的順序:
除了賦值運算符和空合並運算符之外,所有二元運算符都是左關聯的,這意味着操作是從左到右執行的。 例如,x + y + z被評估為(x + y)+ z。
賦值運算符,空合並運算符和條件運算符(?:)是右關聯的,這意味着操作從右到左執行。 例如,x = y = z被評估為x =(y = z)。 可以使用括號控制優先級和關聯性。 例如,x + y * z首先將y乘以z然后將結果添加到x,但是(x + y)* z首先添加x和y,然后將結果乘以z。
這里重要的是評估操作的順序,以便實際評估的是什么
n2 = (n1) + ((n1=n2)*0)
其中(n1)+(..)通過二元運算符從左到右進行求值。
閱讀規范,它會告訴你實情:
7.5.1.2參數列表的運行時評估
參數列表的表達式始終按其編寫順序進行計算。 因此,這個例子
class Test { static void F(int x, int y = -1, int z = -2) { System.Console.WriteLine("x = {0}, y = {1}, z = {2}", x, y, z); } static void Main() { int i = 0; F(i++, i++, i++); F(z: i++, x: i++); } }
產生輸出
x = 0, y = 1, z = 2 x = 4, y = -1, z = 3
如果將代碼更改為:您可以看到它也適用於算術運算:
int n1 = 10, n2=20;
n2 = (n1=n2) * 0 + n1;
現在, n1
和n2
等於20
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.