"基於模式的使用" 和 "使用宣告"
注意
本文是功能規格。 規格可作為功能的設計檔。 其中包含建議的規格變更,以及功能設計和開發期間所需的資訊。 這些文章會發佈,直到提議的規格變更完成並併併入目前的ECMA規格為止。
功能規格與已完成實作之間可能有一些差異。 這些差異是在 語言設計會議(LDM)筆記中擷取的。
冠軍問題:https://github.com/dotnet/csharplang/issues/114
總結
語言會在 using
語句周圍新增兩項新功能,讓資源管理更簡單:除了 using
之外,IDisposable
應該辨識可處置模式,並將 using
宣告新增至語言。
動機
using
聲明是當今資源管理的有效工具,但它需要相當多的儀式。 具有許多資源需管理的方法,可能會因為一連串的 using
語句而在語法上變得繁複。 此語法負擔大到讓大部分的編碼樣式指導方針針對大括號在這種情況下明確設有例外。
using
宣告會移除這裡的絕大部分繁瑣,使 C# 更接近於那些包含資源管理區塊的其他語言。 此外,模式型 using
可讓開發人員擴充可在這裡參與的類型集合。 在許多情況下,可取消建立只為了在 using
語句中使用值的包裝類型的需求。
這些功能一起可讓開發人員簡化和擴充可套用 using
的案例。
詳細設計
使用宣告
語言將允許將 using
新增至局部變數宣告。 這類宣告的效果會與在相同位置的 using
語句中宣告變數相同。
if (...)
{
using FileStream f = new FileStream(@"C:\source\using.md");
// statements
}
// Equivalent to
if (...)
{
using (FileStream f = new FileStream(@"C:\source\using.md"))
{
// statements
}
}
using
本地變數的存續時間會延伸到其被宣告的範圍結尾。 然後,會以宣告局部變數的反向順序處置 using
局部變數。
{
using var f1 = new FileStream("...");
using var f2 = new FileStream("...");
using var f3 = new FileStream("...");
...
// Dispose f3
// Dispose f2
// Dispose f1
}
在 goto
宣告下,using
或任何其他控制流程結構都沒有任何限制。 相反,程式碼的行為就像等效的 using
語句一樣。
{
using var f1 = new FileStream("...");
target:
using var f2 = new FileStream("...");
if (someCondition)
{
// Causes f2 to be disposed but has no effect on f1
goto target;
}
}
在 using
本機宣告中定義的變數將會隱含為唯讀。 這符合在 using
語句中宣告的局部變數行為。
using
宣告的語言文法如下:
local-using-declaration:
'using' type using-declarators
using-declarators:
using-declarator
using-declarators , using-declarator
using-declarator:
identifier = expression
using
宣告的限制:
- 可能不會直接出現在
case
標籤,但必須位於case
卷標內的區塊內。 - 可能不會顯示為
out
變數宣告的一部分。 - 每個宣告項都必須有初始化器。
- 本機類型必須能被隱式轉換為
IDisposable
或滿足using
模式。
以模式為基礎的應用
語言會新增 ref struct
類型的可處置模式概念:這是具有可存取 ref struct
實例方法的 Dispose
。 符合可處置模式的類型可以參與 using
語句或宣告,而不需要實作 IDisposable
。
ref struct Resource
{
public void Dispose() { ... }
}
using (var r = new Resource())
{
// statements
}
這可讓開發人員利用 using
來處理 ref struct
類型。 這些類型無法在 C# 8 中實作介面,因此無法參與 using
語句。
傳統 using
語句的限制同樣適用於此:在 using
中宣告的局部變數是唯讀的,null
值不會引發例外狀況。程式碼生成的不同之處在於,呼叫 Dispose 之前不會轉換成 IDisposable
。
{
Resource r = new Resource();
try {
// statements
}
finally {
if (r != null) r.Dispose();
}
}
為了符合可處置模式,Dispose
方法必須是可存取的實例成員、無參數且具有 void
傳回類型。 它不能是擴充方法。
注意事項
這些考慮都未在 C# 8 中實作
沒有區塊的案例標籤
由於其實際存留期的複雜性,using declaration
在 case
標籤內直接使用是非法的。 為其設定與同一位置的 out var
相同的存留期是一個潛在的解決方案。 被認為功能實作的額外複雜性和工作的簡便替代方法(只需在 case
標籤中新增一個區塊)都不足以證明走這條路線的合理性。
未來擴張
固定局部變數
fixed
語句具有 using
語句的所有屬性,其動機是擁有 using
局部變數的能力。 應考慮將這項功能延伸至 fixed
局部變數。 存留期和排序規則應該同樣適用於此處的 using
和 fixed
。