“using”是C#的关键字吗?

7

如果一个对象在C#中实现了IDisposable,那么可以编写以下代码:

using(DisposableFoo foo = new DisposableFoo())

我一直想知道为什么using看起来像C#关键字,但需要在.Net框架中定义一个接口。在这个上下文中,using是一个关键字(用于定义库导入),还是Microsoft在Visual Studio/.Net框架中重载了它的用法?我不会期望一个C#关键字依赖于一个库。


6
关键字要求其“参数”实现一个接口并没有问题,我不明白其中的问题所在。foreach 也是一个关键字,要求集合实现 IEnumerable 接口。 - sepp2k
3
@sepp2k - 好学究的一个点: 使用foreach不需要IEnumerable。 - Marc Gravell
@Marc Gravell:您是什么意思?根据 MSDN 链接,它说“System.Collections.IEnumerable 或 System.Collections.Generic.IEnumerable<T>”http://msdn.microsoft.com/en-us/library/ttw7t8t6.aspx - user34537
foreach 使用鸭子类型 - http://blogs.msdn.com/b/kcwalina/archive/2007/07/18/ducknotation.aspx - thecoop
@acidzombie24 请查看 @thecoop 添加的链接(此评论仅为点亮信封) - Marc Gravell
啊!这是正确的,但有点诡异。但话说我认为鸭子类型可以检查一个类继承/使用什么接口。我从来没有检查过。有趣。感谢@Marc Gravell和链接@thecoop(还有poke)。 - user34537
7个回答

8

这是一个关键字构造,需要一个IDisposable,因为它作用于对象的Dispose方法。让关键字使用类型也并不过分。例如if是一个关键字,需要一个条件(即Boolean)类型。


7

6
是的,它是一个关键字。C#语言依赖于框架来实现许多关键字。
其他依赖于框架的关键字包括foreach(使用IEnumerable)和LINQ语法(在与LINQ to Objects一起使用时需要System.Linq库)。
即使像intstring这样的关键字也依赖于框架中的类型System.Int32System.String

3
实际上,使用它并不常见是因为它需要一个接口。您可以在没有IEnumerable的情况下使用foreach,并且可以在没有任何特殊库(实际上甚至不需要扩展方法)的情况下使用LINQ。 - Marc Gravell
1
@Mark Gravell:我认为不常见的是foreach循环,因为它使用了一种对于C#来说不典型的鸭子类型,需要实现IEnumerable,但不需要实际的接口。我会澄清一下,LINQ to Objects需要System.Linq库。 - Guffa
@Mark Gravell:LINQ to什么?你会用什么编译器来实现查询? - Guffa
@Guffa,换句话说,你可以认为整个LINQ都是鸭子类型(还有一件小事:我不会收到@Mark的回复,只有@Marc;我只是提一下,以免错过有趣的对话)。 - Marc Gravell
@Guffa,这并不比foreach更像是一个技巧。只要知道编译器遵循的规则... - Marc Gravell
显示剩余5条评论

4

C#与CLI紧密相关,语言规范使用最少的运行时特性。IDisposable用于"using"实际上非常严格,因为在大多数情况下(例如"foreach"、LINQ等),都支持具有和不具有特定运行时实现的支持。

然而,由于IDisposable/using结构对.NET至关重要,我怀疑即使是最小的(但合法的)CLI也无法在没有它的情况下进行,因此在语言规范中提到它并不是一个问题。

其他与特定类型相关的事项:

  • 字符串连接(+ vs string.Concat)
  • 扩展方法
  • 泛型new()
  • [Serializable](映射到IL标志,而不是属性)
  • foreach(但可以在不使用任何接口的情况下使用)
  • 核心基本类型(枚举数组对象值类型委托)
  • 表达式树编译器(但未在规范中描述)

等等


2
是的,.NET框架中的语言和支持程序集之间确实存在耦合。其中最重要的是mscorlib.dll,它是System.IDisposable接口声明的所在地。它非常重要,甚至不会出现在C#项目的引用节点中。C#编译器假定它始终是必需的,并且会自行找到它,除非您使用特殊的/nostdlib编译选项。
其他需要它的语法元素的示例包括:
- 数组[],需要System.Array - [属性],需要System.Attribute - catch,需要System.Exception - 委托,需要System.MulticastDelegate - 类型?,需要System.Nullable<> - params,需要System.ParamArrayAttribute - typeof,需要System.Type 所有基本类型及其在mscorlib中对应的声明之间都存在耦合(如Int32、String、Object、ValueType等)。
稍后的示例包括需要System.Core.dll的Linq查询语法和需要Microsoft.CSharp.dll的dynamic关键字。这些示例并不详尽。
否则,这是相当正常的,即使C编译器也假定可用运行时库具有像memset()和ldiv()这样的原语。

1

我认为使用“使用”关键字可以使您的代码

B b=new B();//can get resource outside using statement but will dispose inside of it
using(A a=new A(),b,C c=new C() //A,B,C are variable of types that implement IDisposable){
SomeCode//Some code to execute
}

进入

B b= new B();
try{ 
A a= new A();
C c= new C();//B isn't here since

SomeCode//execute the code

}finallly{
a.Dispose();//The reason A,B,C need to implement IDisposable
b.Dispose();//is so the compiler can make sure that they can call Dispose()
c.Dispose();
}

为什么要使用IDisposable很重要,因为C#是强类型语言,在幕后它会调用Dispose(),实现IDisposable告诉编译器可以这样做。

类似于foreach与IEnumerable和IEnumerator的关系(调用GetEnumerator(),Current(),MoveNext(),尽管我认为它不仅适用于数组,而且适用于集合类)。


我认为使用(A = new A(), C c = new C())是行不通的。编译器会返回这个信息:"Cannot use more than one type in a for, using, or declaration statement." 我知道这只是你所解释的细节,但没有人提到过,而这正是我在寻找的。 - Alpha

1

using是C#关键字,用作处理IDisposable对象的语法糖。根据MSDN

using块定义了一个范围,在该范围之外,一个或多个对象将被处理。

使用using语句允许程序员指定使用资源的对象何时释放它们。提供给using语句的对象必须实现IDisposable接口。此接口提供Dispose方法,应该释放对象的资源。

令人惊讶的是,即使MSDN也没有明确说明这在幕后是如何发生的。它只说对象必须实现IDisposable接口,该接口在实现接口的对象上提供Dispose方法。因此,为了处理对象,它需要调用对象上的Dispose方法,以清理和释放对象使用的资源。

看一下这个例子...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BlogSamples
{
    class Program
    {
        static void Main(string[] args)
        {
            //Assume that Car is IDisposible.
            using (Car myCar = new Car(1))
            {
                myCar.Run();
            }
        }
    }
}

编译器将代码转换为如下形式...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BlogSamples
{
    class Program
    {
        static void Main(string[] args)
        {
            //Declare myCar object with FullName of the type as seen in IL.
            BlogSamples.Car myCar;

            //Instantiate the object by calling the constructor, matching the flow of IL.
            myCar = new Car(1);

            try
            {
                myCar.Run();
            }
            finally
            {
                if(myCar != null)
                    myCar.Dispose();
            }
        }
    }
}

为了准确理解 using 块的工作原理,我建议您阅读此博客文章

http://www.ruchitsurati.net/index.php/2010/07/28/understanding-%E2%80%98using%E2%80%99-block-in-c/


1
实际上,我认为MSDN没有说明底层发生了什么是件好事。MSDN详细说明了很多实现细节,但并没有解释哪些是实现细节,哪些是必需的(值类型和引用类型时堆栈与堆的区别就是一个经典例子)。如果能更好地分离这些内容会更好一些。如果它详细说明了实现细节,那也不错,但我更喜欢有规范性文本详细说明规范信息。 - Jon Hanna

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接