9 個變數
9.1 一般
變數代表儲存位置。 每個變數都有一個類型,可決定哪些值可以儲存在變數中。 C# 是類型安全的語言,而 C# 編譯程式保證儲存在變數中的值一律屬於適當的類型。 變數的值可以透過指派或使用 ++
和 --
運算符來變更。
變數必須 明確指派 (~9.4),然後才能取得其值。
如下列子集所述,變數會一開始指派或一開始取消指派。 初始指派的變數具有定義完善的初始值,而且一律會被視為絕對指派。 初始未指派的變數沒有初始值。 若要將一開始未指派的變數視為絕對指派在特定位置,則每個可能的執行路徑都會發生指派給變數,導致該位置。
9.2 變數類別
9.2.1 一般
C# 定義八個變數類別:靜態變數、實例變數、陣列元素、值參數、輸入參數、參考參數、輸出參數和局部變數。 後續的子專案會描述每個類別。
範例:在下列程式代碼中
class A { public static int x; int y; void F(int[] v, int a, ref int b, out int c, in int d) { int i = 1; c = a + b++ + d; } }
x
是靜態變數,是實例變數,y
是陣列元素,v[0]
a
是值參數,是參考參數b
,c
是輸出參數,d
是輸入參數,而且i
是局部變數。 end 範例
9.2.2 靜態變數
以修飾詞宣告的 static
欄位是靜態變數。 在建構函式 #static
的包含型別執行之前,靜態變數就會存在,當相關聯的應用程式域停止存在時,就會停止存在。
靜態變數的初始值是變數類型的預設值 (~9.3)。
為了進行明確指派檢查,一開始會考慮指派靜態變數。
9.2.3 實例變數
9.2.3.1 一般
未 static
使用 修飾詞宣告的欄位是實例變數。
類別中的9.2.3.2實例變數
當建立該類別的新實例時,類別的實例變數就會存在,而且當沒有該實例和實例完成項的參考時,就會停止存在。如果有的話,該實例的完成項。
類別之實例變數的初始值是變數型別的預設值 (~9.3)。
為了進行明確指派檢查,一開始會考慮類別的實例變數。
結構中的9.2.3.3實例變數
結構的實例變數與結構所屬的結構變數有完全相同的存留期。 換句話說,當結構類型的變數存在或停止存在時,結構實例變數也一樣。
結構實例變數的初始指派狀態與包含 struct
變數的初始指派狀態相同。 換句話說,當結構變數被視為一開始指派時,其實例變數也一樣,而當結構變數視為初始未指派時,其實例變數同樣不會指派。
9.2.4 陣列元素
當建立數位實例時,陣列的元素就會存在,而且當沒有該陣列實例的參考時就不再存在。
陣列中每個元素的初始值是陣列元素類型的預設值 (~9.3)。
為了進行明確指派檢查,一開始會考慮指派陣列元素。
9.2.5 值參數
在叫用函式成員(方法、實例建構函式、存取子或運算符)或參數所屬的匿名函式時,值參數存在,並以調用中指定的自變數值初始化。 當函式主體執行完成時,值參數通常會停止存在。 不過,如果 value 參數是由匿名函式擷取的(~12.19.6.2),其存留期至少會延長,直到從該匿名函式建立的委派或表達式樹狀結構符合垃圾收集的資格。
為了進行明確指派檢查,一開始會考慮指派值參數。
值參數會在 \15.6.2.2 中進一步討論。
9.2.6 參考參數
參考參數是參考變數(~9.7),在調用函式成員、委派、匿名函式或局部函式時存在,其參考項會初始化為該調用中自變數的變數。 當函式主體執行完成時,參考參數會停止存在。 不同於值參數,參考參數不得擷取 (~9.7.2.9)。
下列明確指派規則適用於參考參數。
注意:輸出參數的規則不同,如 (9.2.7) 中所述。 end note
- 變數在函式成員或委派調用中以參考參數的形式傳遞之前,必須先指派變數 (~9.4)。
- 在函式成員或匿名函式內,參考參數會被視為一開始指派。
參考參數會在 \15.6.2.3.3 中進一步討論。
9.2.7 輸出參數
輸出參數是在調用函式成員、委派、匿名函式或局部函式時存在的參考變數 (~9.7),其參考項會初始化為該調用中作為自變數的變數。 當函式主體執行完成時,輸出參數會停止存在。 不同於值參數,輸出參數不得擷取 (~9.7.2.9)。
下列明確指派規則適用於輸出參數。
注意:參考參數的規則不同,如 (9.2.6) 中所述。 end note
- 變數在函式成員或委派調用中傳遞為輸出參數之前,不需要明確指派。
- 在函式成員或委派調用的正常完成之後,作為輸出參數傳遞的每個變數都會被視為在該執行路徑中指派。
- 在函式成員或匿名函式內,輸出參數一開始會被視為未指派。
- 函式成員、匿名函式或區域函式的每個輸出參數,在函式成員、匿名函式或本機函式正常傳回之前,必須明確指派 (~9.4)。
輸出參數會在 \15.6.2.3.4 中進一步討論。
9.2.8 輸入參數
輸入參數是在調用函式成員、委派、匿名函式或區域函式時存在的參考變數 (~9.7),其參考值會初始化 為該調用中指定為自變數的variable_reference 。 當函式主體執行完成時,輸入參數會停止存在。 不同於值參數,輸入參數不得擷取 (~9.7.2.9)。
下列明確的指派規則適用於輸入參數。
- 變數在函式成員或委派調用中以輸入參數的形式傳遞之前,必須先指派變數(~9.4)。
- 在函式成員、匿名函式或本機函式內,一開始會考慮指派輸入參數。
輸入參數會在 \15.6.2.3.2 中進一步討論。
9.2.9 局部變數
9.2.9.1 一般
局部變數是由try_statement的local_variable_declaration、declaration_expression、foreach_statement或specific_catch_clause宣告。 局部變數也可以由特定類型的模式 s (~11)宣告。 對於foreach_statement,局部變數是反覆項目變數(~13.9.5)。 對於specific_catch_clause,局部變數是例外狀況變數(~13.11)。 一開始會考慮由foreach_statement或specific_catch_clause宣告的局部變數。
local_variable_declaration可能發生在區塊、for_statement、switch_block或using_statement中。
declaration_expression可以做為out
argument_value,而tuple_element,這是解構指派的目標(~12.21.2)。
局部變數的存留期是程序執行的一部分,在此期間,記憶體保證會保留給它。 此存留期會從與其相關聯的範圍專案延伸,至少要等到該範圍以某種方式執行為止。 (輸入封閉區塊 、呼叫方法,或從反覆運算器區塊產生值暫停,但不會結束目前範圍的執行。如果局部變數是由匿名函式所擷取(~12.19.6.2),其存留期至少會延長到從匿名函式建立的委派或表達式樹狀結構,以及參考所擷取變數的任何其他物件,都有資格進行垃圾收集。 如果父範圍以遞歸或反覆方式輸入,則每次都會建立局部變數的新實例,並每次評估其初始化表達式。
注意:每次輸入局部變數範圍時都會具現化。 用戶程式代碼可看見此行為,其中包含匿名方法。 end note
注意:foreach_statement宣告之反覆專案變數 (~13.9.5) 的存留期是該語句的單一反覆運算。 每個反覆項目都會建立新的變數。 end note
注意:局部變數的實際存留期取決於實作。 例如,編譯程式可能會以靜態方式判斷區塊中的局部變數只用於該區塊的一小部分。 使用此分析,編譯程式可能會產生程式代碼,導致變數的記憶體存留期比其包含區塊短。
本機參考變數所參考的記憶體會獨立於該本機參考變數 (~7.9) 的存留期獨立回收。
end note
local_variable_declaration或declaration_expression引進的局部變數不會自動初始化,因此沒有預設值。 這類局部變數一開始會被視為未指派。
注意: 包含初始化表達式的 local_variable_declaration一開始仍然未指派。 宣告的執行行為與變數的指派完全相同({9.4.4.5)。 在執行其初始化表示式之前使用變數;例如,在初始化運算式表達式本身內,或使用 略過初始化表達式的 goto_statement;是編譯時期錯誤:
goto L; int x = 1; // never executed L: x += 1; // error: x not definitely assigned
在局部變數的範圍內,參考其宣告子前面的文字位置中的局部變數是編譯時期錯誤。
end note
9.2.9.2 捨棄
捨棄是沒有名稱的局部變數。 捨棄是由具有標識碼的宣告表達式 ({12.17) 所引進;而且是隱含型別 (_
或_
) 或明確型別 (var _
)。T _
注意:
_
是許多形式宣告的有效標識符。 end note
因為捨棄沒有名稱,所以它所代表之變數的唯一參考是引進它的表達式。
注意:不過,捨棄可以當做輸出自變數傳遞,允許對應的輸出參數表示其相關聯的儲存位置。 end note
一開始不會指派捨棄,因此存取其值一律是錯誤。
範例:
_ = "Hello".Length; (int, int, int) M(out int i1, out int i2, out int i3) { ... } (int _, var _, _) = M(out int _, out var _, out _);
此範例假設範圍中沒有名稱
_
的宣告。要顯示忽略表達式結果之簡單模式的指派
_
。 的呼叫M
會顯示 Tuple 中可用的不同形式捨棄,以及做為輸出參數。end 範例
9.3 預設值
下列變數類別會自動初始化為預設值:
- 靜態變數。
- 類別實例的實例變數。
- 陣列元素。
變數的預設值取決於變數的類型,並依下列方式決定:
- 對於value_type的變數,預設值與value_type預設建構函式 (~8.3.3) 所計算的值相同。
- 對於reference_type的變數,預設值為
null
。
注意:初始化預設值通常是藉由讓記憶體管理員或垃圾收集行程先將記憶體初始化為all-bits-zero,再配置供使用。 基於這個理由,使用全位零來表示 Null 參考是很方便的。 end note
9.4 明確指派
9.4.1 一般
在函式成員或匿名函式的可執行代碼中的某個位置,如果編譯程序可以通過特定的靜態流程分析(§9.4.4)證明變數已被自動初始化或至少執行過一次賦值,那麼該變數可被稱為 明確指派。
注意:非正式說明,明確指派的規則如下:
- 一開始指派的變數 (~9.4.2) 一律被視為絕對指派。
- 如果導致該位置的所有可能執行路徑至少包含下列其中一項,則一開始未指派的變數 (~9.4.3) 會被視為絕對指派於指定位置:
- 簡單指派 (~12.21.2),其中變數是左操作數。
- 調用表達式 (•12.8.10) 或物件建立表達式 (•12.8.17.2),傳遞變數做為輸出參數。
- 若為局部變數,則為包含變數初始化表達式之變數 ({13.6.2) 的局部變數宣告。
上述非正式規則的基礎正式規格描述於 \9.4.2、 \9.4.3 和 \9.4.4。
end note
struct_type變數實例變數的明確指派狀態會個別追蹤,以及統一追蹤。 除了 \9.4.2、\9.4.3 和 \9.4.4 中所述的規則,下列規則適用於struct_type變數及其實例變數:
- 如果實例變數包含 struct_type 變數被視為絕對指派,則會將其視為已指派。
- 如果每個實例變數都被視為絕對指派,則會將struct_type變數視為絕對指派。
明確指派是下列內容中的需求:
變數應該在取得其值的每個位置明確指派。
注意:這可確保永遠不會發生未定義的值。 end note
表達式中變數的出現會被視為取得變數的值,但例外狀況除外
- 變數是簡單指派的左操作數,
- 變數會當做輸出參數傳遞,或
- 變數是 struct_type 變數,並且以成員存取的左操作數的形式發生。
變數應該在傳遞為參考參數的每個位置明確指派。
注意:這可確保叫用的函式成員可以考慮一開始指派的參考參數。 end note
變數應該在傳遞為輸入參數的每個位置明確指派。
注意:這可確保叫用的函式成員可以考慮一開始指派的輸入參數。 end note
函式成員的所有輸出參數,都必須在函式成員傳回的每個位置指派(透過 return 語句或執行到達函式成員主體的結尾)。
附註:這可確保函式成員不會傳回輸出參數中的未定義值,因此可讓編譯程式考慮將變數當做相當於指派給變數的輸出參數的函式成員調用。 end note
this
struct_type實例建構函式的變數,應該在實例建構函式傳回的每個位置明確指派。
9.4.2 初始指派的變數
下列變數類別會分類為一開始指派:
- 靜態變數。
- 類別實例的實例變數。
- 初始指派結構變數的實例變數。
- 陣列元素。
- 值參數。
- 參考參數。
- 輸入參數。
- 子句或語句中
catch
宣告的foreach
變數。
9.4.3 最初未指派的變數
下列變數類別會分類為最初未指派的變數:
- 初始未指派結構變數的實例變數。
- 輸出參數,包括
this
不含建構函式初始化表達式之結構實例建構函式的變數。 - 局部變數,但子句或
catch
語句中foreach
宣告的變數除外。
9.4.4 判斷明確指派的精確規則
9.4.4.1 一般
為了判斷每個使用的變數都已明確指派,編譯程式應該使用相當於這個子程式中所述的進程。
函式成員的主體可以宣告一或多個初始未指派的變數。 對於每個初始未指派的變數 v,編譯器應在函式成員中的下列每個點,為 v 判斷一個 的明確指派狀態。
- 在每個語句的開頭
- 在每個語句的結束點 (~13.2)
- 在每個弧線上,將控制權傳輸至另一個語句或語句的終點
- 在每個表達式的開頭
- 在每個表達式的結尾
v 的明確指派狀態可以是:
- 絕對已指派。 這表示在所有可能控制流程到此點時, v 已獲指派值。
- 未明確指派。 針對類型
bool
表達式結尾的變數狀態,未明確指派之變數的狀態可能會(但不一定)落入下列其中一個子狀態:- 在 true 運算式之後明確指派。 這個狀態表示 如果布爾表達式評估為 true,則絕對會指派 v ,但如果布爾表達式評估為 false,則不一定指派。
- 在 false 表達式之後明確指派。 這個狀態表示 如果布爾表達式評估為 false,則絕對會指派 v ,但如果布爾表達式評估為 true,則不一定指派 v。
下列規則會控管變數 v 的狀態如何在每個位置決定。
9.4.4.2 語句的一般規則
- v 在函式成員主體的開頭未明確指派。
- 任何其他語句開頭 v 的明確指派狀態取決於檢查以該語句開頭為目標之所有控制流程傳輸的 v 明確指派狀態。 如果 (且只有在) v 明確指派給所有這類控制流程傳輸時,則 v 會在語句開頭明確指派。 一組可能的控制流程傳輸,與檢查語句的觸達性(~13.2)相同。
-
v 在 、
block
、checked
unchecked
if
while
do
、for
、foreach
、lock
或using
語句的結束點switch
上,檢查 v 的明確指派狀態,以該語句的結束點為目標的所有控制流程傳輸,來判斷 v 的明確指派狀態。 如果 v 明確指派給所有這類控制流程傳輸,則 v 絕對會在 語句的結束點指派。 否則, v 不會在 語句的結束點明確指派。 一組可能的控制流程傳輸,與檢查語句的觸達性(~13.2)相同。
注意:由於沒有無法連線語句的控件路徑, v 絕對會在任何無法連線的語句開頭指派。 end note
9.4.4.3 區塊語句、已檢查和未核取的語句
控件傳送至區塊中語句清單之第一個語句的明確指派狀態(如果語句清單是空的,則為區塊的結束點)與區塊、 或 checked
語句之前的 vunchecked
相同。
9.4.4.4 Expression 語句
針對表達式語句 stmt ,其中包含表達式 expr:
- v 在 expr 開頭具有與 stmt 開頭相同的明確指派狀態。
- 如果 v 如果在 expr 結束時明確指派,則絕對會在 stmt 的結束點指派;否則,它不會在 stmt 的結束點指派。
9.4.4.5 宣告語句
- 如果 stmt 是不含初始化表達式的宣告語句,則 v 在 stmt 的結束點具有與 stmt 開頭相同的明確指派狀態。
- 如果 stmt 是具有初始化表達式的宣告語句,則 v 的明確指派狀態會判斷為 stmt 是否為語句清單,每個宣告都有一個指派語句,其中每個宣告都有初始化表達式(宣告順序)。
9.4.4.6 If 語句
針對表單的語句 stmt :
if ( «expr» ) «then_stmt» else «else_stmt»
- v 在 expr 開頭具有與 stmt 開頭相同的明確指派狀態。
- 如果 v 在 expr 結束時明確指派,則絕對會在控制流程傳輸上指派給 then_stmt,如果沒有其他子句,則會指派給 else_stmt 或 stmt 的端點。
- 如果 v 在 expr 結尾具有「在 true 運算式之後明確指派」狀態,則會在控制流程傳輸上明確指派給then_stmt,而且如果沒有其他子句,則不會在控制流程傳輸上明確指派給 else_stmt 或 stmt 的端點。
- 如果 v 在 expr 結尾具有「在 false 運算式之後明確指派」狀態,則會在控制流程傳輸上明確指派給else_stmt,而且不會在控制流程傳輸上明確指派給then_stmt。 只有在絕對在then_stmt端點指派 stmt 時,才會在 stmt 的結束點指派它。
- 否則,在控制流程傳輸中,v 會被視為未明確指派給then_stmt或else_stmt,如果沒有其他子句,則視為 stmt 的終點。
9.4.4.7 Switch 語句
switch
針對具有控制表達式 expr 的語句 stmt:
expr 開頭 v 的明確指派狀態與 stmt 開頭的 v 狀態相同。
在案例的 guard 子句開始時 v 的明確指派狀態為
- 如果 v 是switch_label中宣告的模式變數:「絕對已指派」。
- 如果包含該 guard 子句的切換卷標 (~13.8.3) 無法連線:“絕對已指派”。
- 否則,v 的狀態與 expr 之後的 v 狀態相同。
範例:第二個規則使得如果在不可達的程式碼中存取未指派的變數,編譯器不需要報錯。 b 的狀態在無法連線的交換器標籤
case 2 when b
中「已明確指派」。bool b; switch (1) { case 2 when b: // b is definitely assigned here. break; }
end 範例
控制流程傳送至可連線的交換器區塊語句清單上 v 的明確指派狀態為
- 如果控件傳輸是因為 『goto case』 或 'goto default' 語句所造成,則 v 的狀態與該 'goto' 語句開頭的狀態相同。
- 如果控件傳輸是因為參數的標籤所造成
default
,則 v 的狀態會與 expr 之後的 v 狀態相同。 - 如果控件傳輸是由於無法連線的切換標籤,則 v 的狀態會「已明確指派」。
- 如果控件傳輸是因為具有 guard 子句的可連線切換卷標,則 v 的狀態與 guard 子句之後的 v 狀態相同。
- 如果控件傳輸是因為沒有 guard 子句的可連線切換卷標,則 v 的狀態為
- 如果 v 是switch_label中宣告的模式變數:「絕對已指派」。
- 否則,v 的狀態與 expr 之後 v 的統計數據相同。
這些規則的結果是,如果模式變數不是其區段中唯一 可連線的切換標籤,則switch_label 的語句中將會「不明確指派」。
範例:
public static double ComputeArea(object shape) { switch (shape) { case Square s when s.Side == 0: case Circle c when c.Radius == 0: case Triangle t when t.Base == 0 || t.Height == 0: case Rectangle r when r.Length == 0 || r.Height == 0: // none of s, c, t, or r is definitely assigned return 0; case Square s: // s is definitely assigned return s.Side * s.Side; case Circle c: // c is definitely assigned return c.Radius * c.Radius * Math.PI; … } }
end 範例
9.4.4.8 While 語句
針對表單的語句 stmt :
while ( «expr» ) «while_body»
- v 在 expr 開頭具有與 stmt 開頭相同的明確指派狀態。
- 如果 v 在 expr 結束時明確指派,則絕對會在控制流程傳輸上指派給 while_body,並將它指派到 stmt 的終點。
- 如果 v 在 expr 結尾具有「在 true 運算式之後明確指派」狀態,則絕對會在控制流程傳輸上指派給while_body,但未明確指派於 stmt 的終點。
- 如果 v 在 expr 結尾具有「在 false 運算式之後明確指派」狀態,則絕對會在控制流程傳輸上指派至 stmt 的終點,但未明確指派給控制流程傳送給while_body。
9.4.4.9 Do 語句
針對表單的語句 stmt :
do «do_body» while ( «expr» ) ;
- v 在從 stmt 開頭到do_body的控制流程傳輸上具有相同的明確指派狀態,如同 stmt 開頭。
- v 在 expr 開頭具有與do_body結束點相同的明確指派狀態。
- 如果 v 在 expr 結束時明確指派,則絕對會在控制流程傳輸上指派至 stmt 的終點。
- 如果 v 在 expr 結尾具有「在 false 運算式之後明確指派」狀態,則會在控制流程傳輸上明確指派至 stmt 的結束點,但不明確指派給控制流程傳送至do_body。
9.4.4.10 For 語句
針對表單的語句:
for ( «for_initializer» ; «for_condition» ; «for_iterator» )
«embedded_statement»
明確指派檢查會如同撰寫語句一樣完成:
{
«for_initializer» ;
while ( «for_condition» )
{
«embedded_statement» ;
LLoop: «for_iterator» ;
}
}
語句 continue
的目標 for
語句會轉譯為 goto
以標籤 LLoop
為目標的語句。
for
,則明確指派的評估會繼續,就好像在上述擴充中已將for_condition取代為 true。
9.4.4.11 中斷、繼續和goto語句
由 、 或 break
語句所造成continue
之控制流程傳輸上 vgoto
明確指派狀態,與語句開頭的 v 明確指派狀態相同。
9.4.4.12 Throw 語句
針對表單的語句 stmt :
throw «expr» ;
expr 開頭 v 的明確指派狀態與 stmt 開頭 v 的明確指派狀態相同。
9.4.4.13 傳回語句
針對表單的語句 stmt :
return «expr» ;
- expr 開頭 v 的明確指派狀態與 stmt 開頭 v 的明確指派狀態相同。
- 如果 v 是輸出參數,則絕對應指派下列其中一項:
- expr 之後
- 或位於 區塊結尾
finally
的 或try
-finally
try
- ,該區塊catch
-finally
括住return
語句。
針對表單的語句 stmt :
return ;
- 如果 v 是輸出參數,則絕對應指派下列其中一項:
- stmt 之前
- 或位於 區塊結尾
finally
的 或try
-finally
try
- ,該區塊catch
-finally
括住return
語句。
9.4.4.14 Try-catch 語句
針對表單的語句 stmt :
try «try_block»
catch ( ... ) «catch_block_1»
...
catch ( ... ) «catch_block_n»
- try_block開頭 v 的明確指派狀態與 stmt 開頭 v 的明確指派狀態相同。
- v 在catch_block_i開始時的明確指派狀態(針對任何 i)與 v 在 stmt 開頭的明確指派狀態相同。
- 在 stmt 端點的 v 明確指派狀態,如果 (且只有在) v 在 try_block 的結束點和每一個catch_block_i (每一個從 1 到 n 的 i)明確指派,則絕對會指派 v。
9.4.4.15 Try-finally 語句
針對表單的語句 stmt :
try «try_block» finally «finally_block»
- try_block開頭 v 的明確指派狀態與 stmt 開頭 v 的明確指派狀態相同。
- finally_block開頭 v 的明確指派狀態與 stmt 開頭 v 的明確指派狀態相同。
- 在 stmt 結束點的 v 明確指派狀態,如果 (且只有在) 至少有一個下列情況成立時,絕對會指派 v:
- v 絕對會在try_block的端點 指派
- v 絕對會在finally_block的 結束點指派
如果控制流程傳輸(例如 goto
語句)是在try_block內開始,並在try_block外部結束,則如果 v 在finally_block的端點上明確指派,則 v 也會被視為明確指派給該控制流程傳輸。 (這不是唯一的,如果 v 絕對因為此控制流程傳輸的另一個原因被指派,它仍然被視為絕對指派。
9.4.4.16 Try-catch-finally 語句
針對表單的語句:
try «try_block»
catch ( ... ) «catch_block_1»
...
catch ( ... ) «catch_block_n»
finally «finally_block»
明確指派分析會如同語句括 try
-finally
住語句的 try
-catch
語句一樣完成:
try
{
try «try_block»
catch ( ... ) «catch_block_1»
...
catch ( ... ) «catch_block_n»
}
finally «finally_block»
範例:下列範例會示範語句的不同區塊
try
(~13.11) 如何影響明確指派。class A { static void F() { int i, j; try { goto LABEL; // neither i nor j definitely assigned i = 1; // i definitely assigned } catch { // neither i nor j definitely assigned i = 3; // i definitely assigned } finally { // neither i nor j definitely assigned j = 5; // j definitely assigned } // i and j definitely assigned LABEL: ; // j definitely assigned } }
end 範例
9.4.4.17 Foreach 語句
針對表單的語句 stmt :
foreach ( «type» «identifier» in «expr» ) «embedded_statement»
- expr 開頭 v 的明確指派狀態與 stmt 開頭的 v 狀態相同。
- 控制流程傳送至 embedded_statement 或 stmt 端點的 v 明確指派狀態,與 expr 結尾的 v 狀態相同。
9.4.4.18 Using 語句
針對表單的語句 stmt :
using ( «resource_acquisition» ) «embedded_statement»
- resource_acquisition開頭v 的明確指派狀態與 stmt 開頭的 v 狀態相同。
- 控制流程傳送至 embedded_statement v 的明確指派狀態與 v 在resource_acquisition結尾的狀態相同。
9.4.4.19 Lock 語句
針對表單的語句 stmt :
lock ( «expr» ) «embedded_statement»
- expr 開頭 v 的明確指派狀態與 stmt 開頭的 v 狀態相同。
- 控制流程傳送至 embedded_statement v 的明確指派狀態與 expr 結尾的 v 狀態相同。
9.4.4.20 Yield 語句
針對表單的語句 stmt :
yield return «expr» ;
- expr 開頭 v 的明確指派狀態與 stmt 開頭的 v 狀態相同。
- stmt 結尾 v 的明確指派狀態與 expr 結尾的 v 狀態相同。
語句 yield break
對明確指派狀態沒有任何影響。
9.4.4.21 常數表達式的一般規則
下列內容適用於任何常數表達式,並優先於下列各節中可能套用的任何規則:
針對具有 值的 true
常數表示式:
- 如果在 表達式之前明確指派 v ,則在 表達式之後絕對會指派 v 。
- 否則 ,v 會在表達式後面「在 false 運算式之後明確指派」。
範例:
int x; if (true) {} else { Console.WriteLine(x); }
end 範例
針對具有 值的 false
常數表示式:
- 如果在 表達式之前明確指派 v ,則在 表達式之後絕對會指派 v 。
- 否則 v 會在表達式後面「在 true 運算式之後明確指派」。
範例:
int x; if (false) { Console.WriteLine(x); }
end 範例
對於所有其他常數表示式,表達式之後 v 的明確指派狀態與表示式之前的 v 明確指派狀態相同。
9.4.4.22 簡單表達式的一般規則
下列規則適用於這類表達式:常值(§12.8.2)、簡單名稱(§12.8.4)、成員存取表達式(§12.8.7)、非索引基底存取表達式(§12.8.15)、typeof
表達式(§12.8.18)、預設值表達式(§12.8.21)、nameof
表達式(§12.8.23),以及宣告表達式(§12.17)。
- 在這類表達式結尾的 v 明確指派狀態與表達式開頭 v 的明確指派狀態相同。
9.4.4.23 內嵌運算式表達式的一般規則
下列規則適用於這些類型的表達式:括弧表達式(§12.8.5)、Tuple 表達式(§12.8.6)、元素存取表達式(§12.8.12)、具有索引的基底存取表達式(§12.8.15)、遞增和遞減表達式(§12.8.16、§12.9.6)、轉換表達式(§12.9.7)、一元 +
、-
、~
、*
表達式、二元 +
、-
、*
、/
、%
、<<
、>>
、<
、<=
、>
、>=
、==
、!=
、is
、as
、&
、|
、^
表達式(§12.10、§12.11、§12.12、§12.13)、複合指派表達式(§12.21.4)、checked
和 unchecked
表達式(§12.8.20)、數組和委派建立表達式(§12.8.17)和 await
表達式(§12.9.8)。
每一個表達式都有一或多個子表達式,這些子表達式會以固定順序無條件評估。
範例:二元
%
運算符會評估運算子的左側,然後評估右邊。 索引作業會評估索引表達式,然後依左至右的順序評估每個索引表達式。 end 範例
針對表達式 expr,其具有 subexpressions expr₁、exprー、...、exprₓ,依該順序評估:
- expr₁開始時 v 的明確指派狀態與 expr 開頭的明確指派狀態相同。
- 在 expri (i 大於一個) 開始時 v 的明確指派狀態與 expri₋₁ 結尾的明確指派狀態相同。
- expr 結尾 v 的明確指派狀態與 exprₓ 結尾的明確指派狀態相同。
9.4.4.24 調用表達式和物件建立表達式
如果要叫用的方法是沒有實作部分方法宣告的部分方法,或是省略呼叫的條件式方法({22.5.3.2),則調用之後 v 的明確指派狀態與調用之前的 v 明確指派狀態相同。 否則,適用下列規則:
針對表單的呼叫運算式 expr :
«primary_expression» ( «arg₁», «arg₂», … , «argₓ» )
或表單的物件建立表示式 expr :
new «type» ( «arg₁», «arg₂», … , «argₓ» )
- 對於調用表達式,primary_expression之前 v 的明確指派狀態與 expr 之前的 v 狀態相同。
- 對於調用表達式,arg₁ 之前的 v 明確指派狀態與 primary_expression 之後的 v 狀態相同。
- 針對物件建立表達式,arg₁ 之前的 v 明確指派狀態與 expr 之前的 v 狀態相同。
- 對於每個自變數argi,argi之後 v的明確指派狀態是由一般表達式規則所決定,忽略任何
in
、out
或ref
修飾詞。 - 對於任何 i 大於一的自變數 argi,argi 之前的 v 明確指派狀態與 argi₋₁ 之後的 v 狀態相同。
- 如果變數 v 以自變數的形式傳遞
out
(也就是窗體 “out v” 的自變數,則絕對指派 expr 之後的 v 狀態。 否則,expr 之後的 v 狀態與 argₓ 之後的 v 狀態相同。 - 針對陣列初始化表達式 (•12.8.17.5),物件初始化表達式 (!12.8.17.3),集合初始化表達式 (•12 .8.17.4) 和匿名物件初始化表達式 (\12.8.17.7),明確指派狀態取決於這些建構的定義方式。
9.4.4.25 簡單指派表達式
讓表示式 e 中的一組指派目標定義如下:
- 如果 e 是元組運算式,則 e 中的指派目標會是 e 元素之指派目標的聯集。
- 否則,e 中的指派目標為 e。
針對表單的運算式 expr :
«expr_lhs» = «expr_rhs»
- expr_lhs之前的 v 明確指派狀態與 expr 之前的 v 明確指派狀態相同。
- expr_rhs之前的 v 明確指派狀態與 expr_lhs 之後 v 的明確指派狀態相同。
- 如果 v 是expr_lhs的指派目標,則明確指派 expr 之後 v 的明確指派狀態。 否則,如果在結構類型的實例建構函式內發生指派,而 v 是所建構實例上自動實作屬性 P 的隱藏備份字段,而指定 P 的屬性存取是expr_lhs的指示目標,則絕對指派 expr 之後的 v 明確指派狀態。 否則,expr 之後 v 的明確指派狀態與 expr_rhs 之後 v 的明確指派狀態相同。
範例:在下列程式代碼中
class A { static void F(int[] arr) { int x; arr[x = 1] = x; // ok } }
x
在評估為第二個簡單指派的左側之後arr[x = 1]
,變數會被視為絕對指派。end 範例
9.4.4.26 & 運算式
針對表單的運算式 expr :
«expr_first» && «expr_second»
- expr_first之前的 v 明確指派狀態與 expr 之前的 v 明確指派狀態相同。
- expr_second之前 v 的明確指派狀態,只有在expr_first之後的 v 狀態已明確指派或「在 true 運算式之後明確指派」時,才會被指派。 否則,不會明確指派它。
- expr 之後 v 的明確指派狀態取決於:
- 如果已明確指派expr_first之後的 v 狀態,則明確指派 expr 之後的 v 狀態。
- 否則,如果expr_second之後的 v 狀態已明確指派,且 expr_first 之後的 v 狀態會「在 false 表達式後明確指派」,則 expr 之後的 v 狀態會明確指派。
- 否則,如果在expr_second之後的 v 狀態已明確指派或「在 true 運算式之後明確指派」,則 expr 之後的 v 狀態會「在 true expression 之後明確指派」。
- 否則,如果 expr_first 之後的 v 狀態是「在 false expression 之後明確指派」,且在 expr_second 之後的 v 狀態是「在 false expression 之後明確指派」,則 expr 之後的 v 狀態會「在 false expression 之後明確指派」。
- 否則,在 expr 之後的 v 狀態並未明確指派。
範例:在下列程式代碼中
class A { static void F(int x, int y) { int i; if (x >= 0 && (i = y) >= 0) { // i definitely assigned } else { // i not definitely assigned } // i not definitely assigned } }
變數
i
會被視為絕對指派於語句的其中一個內嵌語句中,但不在另一個if
語句中。 在if
方法F
的 語句中,變數i
絕對會在第一個內嵌語句中指派,因為表達式(i = y)
的執行一律在執行這個內嵌語句之前。 相反地,變數i
不會在第二個內嵌語句中明確指派,因為x >= 0
可能已測試 false,導致變數i
未指派。end 範例
9.4.4.27 ||表達式
針對表單的運算式 expr :
«expr_first» || «expr_second»
- expr_first之前的 v 明確指派狀態與 expr 之前的 v 明確指派狀態相同。
- expr_second之前 v 的明確指派狀態,只有在expr_first之後的 v 狀態已明確指派或「在 true 運算式之後明確指派」時,才會被指派。 否則,不會明確指派它。
- expr 之後 v 的明確指派語句取決於:
- 如果已明確指派expr_first之後的 v 狀態,則明確指派 expr 之後的 v 狀態。
- 否則,如果expr_second之後的 v 狀態已明確指派,且 expr_first 之後的 v 狀態會「在 true expression 之後明確指派」,則 expr 之後的 v 狀態會明確指派。
- 否則,如果在expr_second之後的 v 狀態已明確指派或「在 false expression 之後明確指派」,則 expr 之後的 v 狀態會「在 false expression 之後明確指派」。
- 否則,如果 expr_first 之後的 v 狀態是「在 true expression 之後明確指派」,而 expr_ 秒之後的 v 狀態是「在 true expression 之後明確指派」,則 expr 之後的 v 狀態會「在 true expression 之後明確指派」。
- 否則,在 expr 之後的 v 狀態並未明確指派。
範例:在下列程式代碼中
class A { static void G(int x, int y) { int i; if (x >= 0 || (i = y) >= 0) { // i not definitely assigned } else { // i definitely assigned } // i not definitely assigned } }
變數
i
會被視為絕對指派於語句的其中一個內嵌語句中,但不在另一個if
語句中。 在if
方法G
的 語句中,變數i
絕對會在第二個內嵌語句中指派,因為表達式(i = y)
的執行一律在執行這個內嵌語句之前。 相反地,變數i
不會在第一個內嵌語句中明確指派,因為x >= 0
可能已測試 true,導致變數i
未指派。end 範例
9.4.4.28 ! 運算式
針對表單的運算式 expr :
! «expr_operand»
- expr_operand之前的 v 明確指派狀態與 expr 之前的 v 明確指派狀態相同。
- expr 之後 v 的明確指派狀態取決於:
- 如果明確指派expr_operand之後的狀態
v
,則絕對指派 expr 之後的狀態v
。 - 否則,如果 expr_operand 之後的狀態
v
是「在 false expression 之後明確指派」,則 expr 之後的狀態v
會「在 true expression 之後明確指派」。 - 否則,如果 expr_operand 之後的狀態
v
是「在 true expression 之後明確指派」,則 expr 之後的 v 狀態會「在 false expression 之後明確指派」。 - 否則,expr
v
的狀態不會明確指派。
- 如果明確指派expr_operand之後的狀態
9.4.4.29 ?? 運算式
針對表單的運算式 expr :
«expr_first» ?? «expr_second»
- expr_first之前的 v 明確指派狀態與 expr 之前的 v 明確指派狀態相同。
- expr_second之前的 v 明確指派狀態與 expr_first 之後 v 的明確指派狀態相同。
- expr 之後 v 的明確指派語句取決於:
9.4.4.30 ?: 運算式
針對表單的運算式 expr :
«expr_cond» ? «expr_true» : «expr_false»
- expr_cond之前的 v 明確指派狀態與 expr 之前的 v 狀態相同。
- 如果expr_cond之後的 v 狀態已明確指派或「在 true 運算式之後明確指派」,則絕對會指派 expr_true v 的明確指派狀態。
- 如果expr_cond之後的 v 狀態明確指派或「在 false 表達式之後明確指派」,則絕對會指派expr_false之前的 v 狀態。
- expr 之後 v 的明確指派狀態取決於:
9.4.4.31 匿名函式
針對具有主體(區塊或表達式)主體的lambda_expression或anonymous_method_expressionexpr:
- 參數的明確指派狀態與具名方法的參數相同(•9.2.6,9.2.7,9.2.8)。
- 主體之前外部變數 v 的明確指派狀態與 expr 之前的 v 狀態相同。 也就是說,外部變數的明確指派狀態繼承自匿名函式的內容。
- expr 之後外部變數 v 的明確指派狀態與 expr 之前的 v 狀態相同。
範例:範例
class A { delegate bool Filter(int i); void F() { int max; // Error, max is not definitely assigned Filter f = (int n) => n < max; max = 5; DoWork(f); } void DoWork(Filter f) { ... } }
會產生編譯時期錯誤,因為不會在宣告匿名函式的位置明確指派 max。
end 範例
範例:範例
class A { delegate void D(); void F() { int n; D d = () => { n = 1; }; d(); // Error, n is not definitely assigned Console.WriteLine(n); } }
也會產生編譯時期錯誤,因為匿名函式中的 指派
n
不會影響匿名函式外部的明確指派狀態n
。end 範例
9.4.4.32 擲回表達式
針對表單的運算式 expr :
throw
thrown_expr
- thrown_expr之前的 v 明確指派狀態與 expr 之前的 v 狀態相同。
- expr 之後 v 的明確指派狀態是「絕對指派」。
9.4.4.33 局部函數中變數的規則
區域函式會在其父方法的內容中進行分析。 本機函式有兩個控制流程路徑:函式呼叫和委派轉換。
每個本機函式主體的明確指派會針對每個呼叫月臺分別定義。 在每個調用中,如果局部函數在呼叫點明確指派變數,就會被視為絕對指派。 此時,控制流程路徑也存在於本機函式主體,並視為可連線。 在呼叫區域函式之後,在每次控制點明確指派的變數都會被視為在呼叫位置之後明確指派函式(return
語句、 yield
語句、 await
表達式)。
委派轉換具有本機函式主體的控制流程路徑。 如果已擷取的變數在轉換之前明確指派,則絕對會為主體指派這些變數。 本機函式指派的變數在轉換之後不會被視為指派。
注意:上述表示會針對每個本機函式調用或委派轉換的明確指派重新分析主體。 編譯程式不需要在每次叫用或委派轉換時重新分析本機函式的主體。 實作必須產生相當於該描述的結果。 end note
範例:下列範例示範本機函式中擷取變數的明確指派。 如果本機函式在寫入之前讀取擷取的變數,則必須先指派擷取的變數,才能呼叫local函式。 本機函式
F1
會s
讀取而不指派它。 如果在F1
明確指派之前s
呼叫 ,就會發生錯誤。F2
i
在讀取之前指派 。 在明確指派之前i
,可以呼叫它。 此外,F3
可能會在 之後F2
呼叫 ,因為s2
已在 中F2
明確指派 。void M() { string s; int i; string s2; // Error: Use of unassigned local variable s: F1(); // OK, F2 assigns i before reading it. F2(); // OK, i is definitely assigned in the body of F2: s = i.ToString(); // OK. s is now definitely assigned. F1(); // OK, F3 reads s2, which is definitely assigned in F2. F3(); void F1() { Console.WriteLine(s); } void F2() { i = 5; // OK. i is definitely assigned. Console.WriteLine(i); s2 = i.ToString(); } void F3() { Console.WriteLine(s2); } }
end 範例
9.4.4.34 is-pattern 運算式
針對表單的運算式 expr :
expr_operand為模式
- expr_operand之前的 v 明確指派狀態與 expr 之前的 v 明確指派狀態相同。
- 如果在模式中宣告變數 'v',則 expr 之後的明確指派狀態會「在 true 時明確指派」。
- 否則,expr 之後 'v' 的明確指派狀態與expr_operand之後 'v' 的明確指派狀態相同。
9.5 變數參考
variable_reference是分類為變數的表達式。 variable_reference表示可以同時存取以擷取目前值及儲存新值的儲存位置。
variable_reference
: expression
;
注意:在 C 和 C++ 中 ,variable_reference 稱為 左值。 end note
9.6 變數參考的不可部分完成性
下列數據類型的讀取和寫入應該是不可部分完成的:bool
、、、char
byte
sbyte
short
ushort
uint
、、 int
float
和 參考型別。 此外,在上一個清單中具有基礎型別的列舉型別讀取和寫入也應該不可部分完成。 讀取和寫入其他類型,包括 long
、 ulong
、 double
、 和 decimal
以及使用者定義型別,不需要不可部分完成。 除了針對該目的所設計的連結庫函式之外,不保證不可部分完成的讀取修改-寫入,例如遞增或遞減的情況。
9.7 參考變數並傳回
9.7.1 一般
參考變數是參考另一個變數的變數,稱為引用者(\9.2.6)。 參考變數是使用 修飾詞宣告的 ref
局部變數。
參考變數會將 variable_reference (\9.5) 儲存至其參考值,而不是其參考值。 當使用參考變數時,會傳回值時,會傳回參考項的值;同樣地,當參考變數是指派的目標時,它就是指派的目標。 參考變數所參考的變數,也就是其參考項的預存 variable_reference ,可以使用 ref 指派 (= ref
) 來變更。
範例: 下列範例示範參考變數,其參考項是陣列元素:
public class C { public void M() { int[] arr = new int[10]; // element is a reference variable that refers to arr[5] ref int element = ref arr[5]; element += 5; // arr[5] has been incremented by 5 } }
end 範例
參考傳回是傳回自 returns-by-ref 方法 (~15.6.1) 所傳回的variable_reference。 這個 variable_reference 是參考傳回的參考。
範例: 下列範例示範參考傳回,其參考項是陣列欄位的元素:
public class C { private int[] arr = new int[10]; public ref readonly int M() { // element is a reference variable that refers to arr[5] ref int element = ref arr[5]; return ref element; // return reference to arr[5]; } }
end 範例
9.7.2 參考安全內容
9.7.2.1 一般
所有參考變數都遵守安全規則,以確保參考變數的 ref-safe-context 不大於參考值的 ref-safe-context。
注意:安全內容的相關概念定義於 (~16.4.12),以及相關聯的條件約束。 end note
對於任何變數,該變數的 ref-safe-context 是該變數variable_reference (9.5) 的有效內容。 參考變數的參考應具有至少與參考變數本身的 ref-safe-context 一樣寬的 ref-safe-context。
附注:編譯器會透過程式文本的靜態分析來判斷 ref-safe-context。 ref-safe-context 會反映運行時間變數的存留期。 end note
有三個 ref-safe-contexts:
宣告塊:指向局部變數的 variable_reference 的 ref-safe-context(§9.2.9.1)是該局部變數的範圍(§13.6.2),包括該範圍內的任何巢狀 embedded-statement。
局部變數variable_reference只有在參考變數在該變數的 ref-safe-context 內宣告時,參考變數才為參考變數的有效參考。
function-member:在函式內, variable_reference 至下列任一項的 function-member 具有 ref-safe-context of function-member:
- 函式成員宣告上的值參數 ({15.6.2.2),包括類別成員函式的隱含
this
; 和 - 結構成員函式的隱含參考 (
ref
)參數(~15.6.2.3.3.3),this
以及其字段。
只有參考變數在相同函式成員中宣告參考變數時,具有 ref-safe-context of function-member 的variable_reference才有效參考。
- 函式成員宣告上的值參數 ({15.6.2.2),包括類別成員函式的隱含
caller-context:在函式中,對下列任一項variable_reference具有 caller-context 的 ref-safe-context:
- 參考參數 (~9.2.6) 非結構成員函式的隱含
this
參數; - 這類參數的成員欄位和元素;
- 類別類型參數的成員欄位;和
- 陣列類型的參數元素。
- 參考參數 (~9.2.6) 非結構成員函式的隱含
具有 caller-context 之 ref-safe-context 的variable_reference可以是參考傳回的參考。
這些值會形成從最窄(宣告區塊)到最寬(呼叫端內容)的巢狀關聯性。 每個巢狀區塊都代表不同的內容。
範例:下列程式代碼顯示不同 ref-safe-contexts 的範例。 宣告會顯示參考的 ref-safe-context,以做為變數的初始化表達式
ref
。 這些範例顯示參考傳回的 ref-safe-context:public class C { // ref safe context of arr is "caller-context". // ref safe context of arr[i] is "caller-context". private int[] arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // ref safe context is "caller-context" public ref int M1(ref int r1) { return ref r1; // r1 is safe to ref return } // ref safe context is "function-member" public ref int M2(int v1) { return ref v1; // error: v1 isn't safe to ref return } public ref int M3() { int v2 = 5; return ref arr[v2]; // arr[v2] is safe to ref return } public void M4(int p) { int v3 = 6; // context of r2 is declaration-block, // ref safe context of p is function-member ref int r2 = ref p; // context of r3 is declaration-block, // ref safe context of v3 is declaration-block ref int r3 = ref v3; // context of r4 is declaration-block, // ref safe context of arr[v3] is caller-context ref int r4 = ref arr[v3]; } }
end 範例。
範例:對於
struct
類型,隱含this
參數會以參考參數的形式傳遞。 當函式成員傳回時,類型欄位struct
的 ref-safe-context 可防止傳回這些欄位。 此規則會防止下列程式代碼:public struct S { private int n; // Disallowed: returning ref of a field. public ref int GetN() => ref n; } class Test { public ref int M() { S s = new S(); ref int numRef = ref s.GetN(); return ref numRef; // reference to local variable 'numRef' returned } }
end 範例。
9.7.2.2 局部變數 ref 安全內容
若為局部變數 v
:
- 如果
v
是參考變數,則其 ref-safe-context 與初始化表達式的 ref-safe-context 相同。 - 否則,其 ref-safe-context 是 declaration-block。
9.7.2.3 參數 ref 安全內容
針對 參數 p
:
- 如果
p
是參考或輸入參數,則其 ref-safe-context 是呼叫端內容。 如果p
輸入參數,則無法寫ref
入的形式傳回,但可以傳回為ref readonly
。 - 如果
p
是輸出參數,則其 ref-safe-context 是呼叫端內容。 - 否則,如果
p
是this
結構類型的參數,則其 ref-safe-context 是 function-member。 - 否則,參數是值參數,而其 ref-safe-context 則是 function-member。
9.7.2.4 字段 ref 安全內容
針對指定欄位參考的變數, e.F
- 如果
e
是參考型別,則其 ref-safe-context 是呼叫端內容。 - 否則,如果
e
為實值型別,則其 ref-safe-context 與 的e
ref-safe-context 相同。
9.7.2.5 運算符
條件運算子 (^12.18), c ? ref e1 : ref e2
和參考指派運算符 = ref e
(^12.21.1) 具有參考變數作為操作數,併產生參考變數。 對於這些運算符,結果的 ref-safe-context 是所有 ref
操作數之 ref-safe-context 之間的最窄內容。
9.7.2.6 函式調用
對於由 ref-returning 函式調用產生的變數 c
,其 ref-safe-context 是下列內容中最窄的內容:
- 呼叫端內容。
- 所有
ref
、out
和in
自變數表達式的 ref 安全內容(不包括接收者)。 - 針對每個輸入參數,如果有對應的表達式是變數,而且變數類型與參數類型之間有識別轉換,則變數的 ref-safe-context,否則為最接近的封入內容。
- 所有自變數表達式的安全內容 (~16.4.12)(包括接收者)。
範例:處理程式碼所需的最後一個項目符號,例如
ref int M2() { int v = 5; // Not valid. // ref safe context of "v" is block. // Therefore, ref safe context of the return value of M() is block. return ref M(ref v); } ref int M(ref int p) { return ref p; }
end 範例
上述規則會將屬性調用和索引器調用視為get
set
基礎存取子的函式調用。 本機函式調用是函式調用。
9.7.2.7 值
值的 ref-safe-context 是最接近的封入內容。
注意:這會在叫用中發生,例如
M(ref d.Length)
,其中d
的類型為dynamic
。 它也與對應至輸入參數的自變數一致。 end note
9.7.2.8 建構函式調用
new
叫用建構函式的表達式會遵守與方法調用(~9.7.2.6)相同的規則,該規則被視為傳回所建構的類型。
9.7.2.9 參考變數的限制
- 參考參數、輸出參數、輸入參數
ref
、本機參數,或是類型的參數或區域ref struct
,都不得由 Lambda 運算式或區域函式擷取。 - 參考參數、輸出參數、輸入參數,或型別的參數
ref struct
都不得為反覆運算器方法或async
方法的自變數。 - 在
ref
語句或ref struct
表達式的點yield return
上,類型或類型的區域await
都不得在內容中。 - 對於 ref 重新指派
e1 = ref e2
,的e2
ref-safe-context 至少應與 ref-safe-contexte1
寬。 - 對於 ref return 語句
return ref e1
,的 ref 安全內容e1
應該是呼叫端內容。