注意
本文是功能規格。 規格可作為功能的設計檔。 其中包含建議的規格變更,以及功能設計和開發期間所需的資訊。 這些文章會發佈,直到提議的規格變更完成並併併入目前的ECMA規格為止。
功能規格與已完成實作之間可能有一些差異。 這些差異被記錄在相關的 語言設計會議(LDM)中。
冠軍問題:https://github.com/dotnet/csharplang/issues/3194
總結
允許 foreach
循環辨識滿足 foreach 模式的擴充方法 GetEnumerator
,並在一般會導致錯誤的情況下對表達式進行迴圈執行。
動機
這將使 foreach
與 C# 中其他功能的實作方式一致,包括非同步和以模式為基礎的解構。
詳細設計
規格變更相對簡單。 我們將 The foreach statement
§13.9.5 一節修改為此文字:
foreach 語句的編譯時間處理會先決定表達式的 集合類型、列舉值類型 和 項目類型。 此判斷如下進行:
如果
X
的類型 是陣列類型,則存在一個從X
到IEnumerable
介面的隱式參考轉換(因為System.Array
實作這個介面)。 集合類型 是IEnumerable
介面,列舉值類型 是IEnumerator
介面,而 項目類型 是數位類型X
的項目類型。如果
X
的類型是dynamic
,則會從 表示式 隱含轉換成IEnumerable
介面(§10.2.10)。 集合類型 是IEnumerable
介面,而 列舉值類型 是IEnumerator
介面。 如果將var
識別元指定為 local_variable_type,則 項目類型dynamic
,否則為object
。否則,請判斷類型
X
是否有適當的GetEnumerator
方法:
- 使用標識碼
X
且沒有類型自變數,對類型GetEnumerator
執行成員查閱。 如果搜尋成員未能產生匹配結果、產生不明確的匹配或產生非方法群組的匹配,應檢查可列舉的介面,如下所述。 如果成員查找產生除方法群組外的任何結果,或沒有找到匹配,建議發出警告。- 使用產生的方法群組和空的自變數清單來執行多載解析。 如果多載解析沒有產生任何適用的方法、產生模棱兩可的方法,或產生單一最佳方法,但該方法為靜態或不公用,請檢查可列舉的介面,如下所示。 如果多載解析不是產生明確的公用實例方法或無可應用的方法,建議發出警告。
- 如果
E
方法的傳回型別GetEnumerator
不是類別、結構或介面類型,則會產生錯誤,而且不會採取任何進一步的步驟。- 成員查找會在具有識別符
E
且沒有類型引數的Current
上執行。 如果成員查詢沒有產生任何匹配的對象,則會產生錯誤;或者如果結果不是允許讀取的公用實例屬性,則同樣會產生錯誤,且不再進行任何其他步驟。- 成員查找會在具有識別符
E
且沒有類型引數的MoveNext
上執行。 如果成員查閱不會產生任何相符專案,則結果為錯誤,或結果為方法群組以外的任何專案,則會產生錯誤,而且不會採取任何進一步的步驟。- 多載解析是在具有空自變數清單的方法群組上執行。 如果多載解析沒有產生任何適用的方法、造成模棱兩可,或產生單一最佳方法,但該方法為靜態或非公用方法,或其傳回類型未
bool
,則會產生錯誤,而且不會採取任何進一步的步驟。- 集合類型 是
X
、列舉值類型 是E
,而 項目類型 是Current
屬性的類型。否則,請檢查可列舉的介面:
- 如果在從
Ti
隱含轉換成X
的所有類型IEnumerable<Ti>
之間,有一個唯一的類型T
,使得T
不是dynamic
,而所有其他Ti
都有從IEnumerable<T>
到IEnumerable<Ti>
的隱含轉換,則 集合類型 為 介面IEnumerable<T>
。 列舉值類型 是介面IEnumerator<T>
,而 項目類型T
。- 否則,如果有多個這類類型
T
,則會產生錯誤,而且不會採取任何進一步的步驟。- 否則,如果從
隱含轉換成 介面,則 為這個介面,則 集合類型 為介面 列舉值類型 ,而 項目類型 。 否則,請判斷類型 『X』 是否具有適當的
GetEnumerator
擴充方法:
- 使用標識碼
X
,對類型GetEnumerator
執行擴充方法查閱。 如果成員查閱未產生匹配項,或者產生模棱兩可的結果,或者產生與方法群組不符的匹配項,將會發生錯誤,不會進行任何進一步的步驟。 如果成員查閱產生方法群組以外的任何專案,或沒有任何相符專案,建議發出警告。- 使用產生的方法群組,並以
X
類型的單一自變數執行多載解析。 如果多載解析沒有產生適用的方法、導致模稜兩可,或是找到了單一最佳方法但該方法無法存取,則會產生錯誤並不會進行任何進一步的步驟。
- 如果
X
是結構類型,而且 ref 種類in
,則此解析允許 ref 傳遞第一個自變數。- 如果
E
方法的傳回型別GetEnumerator
不是類別、結構或介面類型,則會產生錯誤,而且不會採取任何進一步的步驟。- 成員查找會在具有識別符
E
且沒有類型引數的Current
上執行。 如果成員查詢沒有產生任何匹配的對象,則會產生錯誤;或者如果結果不是允許讀取的公用實例屬性,則同樣會產生錯誤,且不再進行任何其他步驟。- 成員查找會在具有識別符
E
且沒有類型引數的MoveNext
上執行。 如果成員查閱不會產生任何相符專案,則結果為錯誤,或結果為方法群組以外的任何專案,則會產生錯誤,而且不會採取任何進一步的步驟。- 多載解析是在具有空自變數清單的方法群組上執行。 如果多載解析沒有產生任何適用的方法、造成模棱兩可,或產生單一最佳方法,但該方法為靜態或非公用方法,或其傳回類型未
bool
,則會產生錯誤,而且不會採取任何進一步的步驟。- 集合類型 是
X
、列舉值類型 是E
,而 項目類型 是Current
屬性的類型。否則,會產生錯誤,而且不會採取任何進一步的步驟。
針對 await foreach
,規則會同樣修改。 該規格所需的唯一變更是從描述中移除 Extension methods do not contribute.
行,因為該規格的其餘部分是以上述規則為基礎,並使用不同的名稱取代模式方法。
缺點
每個變更都會為語言增加額外的複雜度,這可能會讓原本不適用於 foreach
與 foreach
的事物變得適用,例如 Range
。
替代方案
無所事事。
未解決的問題
此刻沒有。