"基于模式使用" 和 "using 声明""pattern-based using" and "using declarations"

总结Summary

语言将添加两个新功能 using 来使资源管理更简单: using 应识别一种可释放模式 IDisposable ,并向 using 语言添加声明。The language will add two new capabilities around the using statement in order to make resource management simpler: using should recognize a disposable pattern in addition to IDisposable and add a using declaration to the language.

动机Motivation

using 语句是当今资源管理的有效工具,但它需要很多工作。The using statement is an effective tool for resource management today but it requires quite a bit of ceremony. 具有多个要管理的资源的方法可以从语法上陷入一系列 using 语句。Methods that have a number of resources to manage can get syntactically bogged down with a series of using statements. 这种语法负担足以表明,大多数编码样式准则对此方案在大括号附近有一个例外。This syntax burden is enough that most coding style guidelines explicitly have an exception around braces for this scenario.

using 声明消除了这里的许多仪式,并与包含资源管理块的其他语言一起获取了 c #。The using declaration removes much of the ceremony here and gets C# on par with other languages that include resource management blocks. 此外,基于模式的 using 允许开发人员展开可参与的类型集。Additionally the pattern-based using lets developers expand the set of types that can participate here. 在许多情况下,无需创建仅存在的包装类型,就可以在语句中使用值 usingIn many cases removing the need to create wrapper types that only exist to allow for a values use in a using statement.

借助这些功能,开发人员可简化和扩展 using 可应用的方案。Together these features allow developers to simplify and expand the scenarios where using can be applied.

详细设计Detailed Design

using 声明using declaration

语言将允许 using 添加到局部变量声明。The language will allow for using to be added to a local variable declaration. 此类声明与在同一位置的语句中声明变量的效果相同 usingSuch a declaration will have the same effect as declaring the variable in a using statement at the same location.

if (...) 
{ 
   using FileStream f = new FileStream(@"C:\users\jaredpar\using.md");
   // statements
}

// Equivalent to 
if (...) 
{ 
   using (FileStream f = new FileStream(@"C:\users\jaredpar\using.md")) 
   {
    // statements
   }
}

本地的生存期 using 将扩展到声明它的范围的末尾。The lifetime of a using local will extend to the end of the scope in which it is declared. 然后,将 using 按声明的反向顺序来处理局部变量。The using locals will then be disposed in the reverse order in which they are declared.

{ 
    using var f1 = new FileStream("...");
    using var f2 = new FileStream("..."), f3 = new FileStream("...");
    ...
    // Dispose f3
    // Dispose f2 
    // Dispose f1
}

goto在声明中没有围绕或任何其他控制流构造的限制 usingThere are no restrictions around goto, or any other control flow construct in the face of a using declaration. 相反,代码的行为与对等效语句的行为一样 usingInstead the code acts just as it would for the equivalent using statement:

{
    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 将隐式为只读。A local declared in a using local declaration will be implicitly read-only. 这与语句中声明的局部变量的行为匹配 usingThis matches the behavior of locals declared in a using statement.

声明的语言语法 using 如下:The language grammar for using declarations will be the following:

local-using-declaration:
  using type using-declarators

using-declarators:
  using-declarator
  using-declarators , using-declarator
  
using-declarator:
  identifier = expression

关于声明的限制 usingRestrictions around using declaration:

  • 不能直接显示在标签 case 内,而是必须位于标签内的 块 case 内。May not appear directly inside a case label but instead must be within a block inside the case label.
  • 可能不会显示为变量声明的 out 一部分。May not appear as part of an out variable declaration.
  • 每个声明符必须具有初始值设置项。Must have an initializer for each declarator.
  • 本地类型必须可隐式转换为 IDisposable 或满足 using 模式。The local type must be implicitly convertible to IDisposable or fulfill the using pattern.

使用基于模式的pattern-based using

语言将添加可释放模式的概念:即具有可访问实例方法 Dispose 的类型。The language will add the notion of a disposable pattern: that is a type which has an accessible Dispose instance method. 适合可释放模式的类型可以参与语句或 using 声明,而无需实现 IDisposableTypes which fit the disposable pattern can participate in a using statement or declaration without being required to implement IDisposable.

class Resource
{ 
    public void Dispose() { ... }
}

using (var r = new Resource())
{
    // statements
}

这使开发人员能够 using 利用许多新方案:This will allow developers to leverage using in a number of new scenarios:

  • ref struct:这些类型目前无法实现接口,因此不能参与 using 语句。ref struct: These types can't implement interfaces today and hence can't participate in using statements.
  • 扩展方法将使开发人员能够增强其他程序集中的类型以参与 using 语句。Extension methods will allow developers to augment types in other assemblies to participate in using statements.

如果类型可以隐式转换为 并且也适合可释放模式, IDisposableIDisposable 为首选。In the situation where a type can be implicitly converted to IDisposable and also fits the disposable pattern, then IDisposable will be preferred. 尽管此方法采用与接口 (相反的方法,但) foreach 向后兼容性是必需的。While this takes the opposite approach of foreach (pattern preferred over interface) it is necessary for backwards compatibility.

传统语句的相同限制也适用于此处:在 中声明的局部变量是只读的,值不会导致引发异常 using using null ,等等。代码生成将有所不同,因为调用 Dispose 之前不会 IDisposable 强制转换到 :The same restrictions from a traditional using statement apply here as well: local variables declared in the using are read-only, a null value will not cause an exception to be thrown, etc ... The code generation will be different only in that there will not be a cast to IDisposable before calling Dispose:

{
      Resource r = new Resource();
      try {
            // statements
      }
      finally {
            if (r != null) r.Dispose();
      }
}

为了适应可释放模式, Dispose 该方法必须可访问、无参数且具有 void 返回类型。In order to fit the disposable pattern the Dispose method must be accessible, parameterless and have a void return type. 没有其他限制。There are no other restrictions. 这明确意味着可以在此处使用扩展方法。This explicitly means that extension methods can be used here.

注意事项Considerations

不带块的 case 标签case labels without blocks

直接 using declaration 在标签内部非法 case ,因为在其实际生存期周围出现复杂情况。A using declaration is illegal directly inside a case label due to complications around its actual lifetime. 一种可能的解决方案是只为它提供与位于同一 out var 位置的 相同的生存期。One potential solution is to simply give it the same lifetime as an out var in the same location. 这会被视为功能实现的额外复杂性,并使 (只需将块添加到标签中, case) 没有采用此路线的理由。It was deemed the extra complexity to the feature implementation and the ease of the work around (just add a block to the case label) didn't justify taking this route.

未来扩展Future Expansions

固定局部变量fixed locals

fixed语句包含的所有语句属性都可以 using 具有 using 局部变量。A fixed statement has all of the properties of using statements that motivated the ability to have using locals. 应考虑将此功能扩展到 fixed 局部变量。Consideration should be given to extending this feature to fixed locals as well. 生存期和顺序规则应该同样适用于 usingfixed 此处。The lifetime and ordering rules should apply equally well for using and fixed here.