为什么'使用'改善C#表现

Wer*_*ght 13 c# compiler-construction dispose idisposable using

在大多数情况下,C#编译器似乎可以Dispose()自动调用.像大多数使用模式的情况看起来像:

public void SomeMethod()
{
    ...

    using (var foo = new Foo())
    {
        ...
    }

    // Foo isn't use after here (obviously).
    ...
}
Run Code Online (Sandbox Code Playgroud)

因为foo没有使用(这是一个非常简单的检测),并且由于它没有作为参数提供给另一个方法(这是一个适用于许多用例并且可以扩展的假设),编译器可以自动并立即调用Dispose()而无需开发人员需要做到这一点.

这意味着在大多数情况下,using如果编译器做了一些聪明的工作,那么它就没用了.IDisposable似乎低水平足以让我被编译器考虑在内.

现在为什么不这样做?这不会改善性能(如果开发人员...... 很脏).

Ada*_*son 21

几点:

呼叫Dispose不会提高性能.IDisposable适用于使用运行时无法解决的有限和/或非托管资源的场景.

关于编译器如何处理IDisposable代码中的对象,没有明确而明显的机制.什么使它成为自动处置的候选者,什么不是?如果实例(或可能)暴露在方法之外?没有什么可说的,因为我将一个对象传递给另一个函数或类,我希望它可以在方法的范围之外使用

例如,考虑一个工厂模式,它接受Stream并反序列化一个类的实例.

public class Foo
{
    public static Foo FromStream(System.IO.Stream stream) { ... }
}
Run Code Online (Sandbox Code Playgroud)

我称之为:

Stream stream = new FileStream(path);

Foo foo = Foo.FromStream(stream);
Run Code Online (Sandbox Code Playgroud)

现在,我可能会或可能不希望Stream在方法退出时将其处理掉.如果Foo工厂从中读取所有必要的数据Stream并且不再需要它,那么我希望它被处理掉.如果Foo对象必须保留流并在其生命周期内使用它,那么我不希望它被丢弃.

同样,从构造函数以外的其他东西检索的实例如何Control.CreateGraphics().这些实例可能存在于代码之外,因此编译器不会自动处理它们.

给予用户控制(并提供类似using块的习语)使得用户的意图清晰并且使得更容易发现IDisposable实例未被正确处理的地方.如果编译器要自动处理某些实例,那么调试将变得更加困难,因为开发人员不得不破译自动处理规则如何应用于使用IDisposable对象的每个代码块.

最后,IDisposable在一个类型上实现有两个原因(按惯例).

  1. 您正在使用非托管资源(意味着您正在进行P/Invoke调用,返回类似于必须由其他P/Invoke调用释放的句柄)
  2. IDisposable当该对象的生命周期结束时,您的类型具有应该被处置的实例.

在第一种情况下,如果开发人员没有这样做(这是为了防止内存和处理泄漏),所有这些类型都应该实现一个终结器来调用Dispose和释放所有非托管资源.


Jus*_*ner 16

垃圾收集(虽然与IDisposable没有直接关系,清理未使用的对象)并不那么简单.

让我重新说一下这个.自动呼叫Dispose()并不那么简单.它也不会直接提高性能.稍后再详细说明.

如果您有以下代码:

public void DoSomeWork(SqlCommand command)
{
    SqlConnection conn = new SqlConnection(connString);

    conn.Open();

    command.Connection = conn;

    // Rest of the work here
 }
Run Code Online (Sandbox Code Playgroud)

编译器如何知道您何时使用该conn对象?或者,如果您传递了一些其他方法的引用?

明确地调用Dispose()或使用using块清楚地表明您的意图并强制事情得到适当的清理.

现在,回到表演.简单地调用Dispose()Object并不能保证性能的提升.完成对象后,该Dispose()方法用于"清理"资源.

使用未受管理的资源时可能会出现性能提升.如果托管对象未正确处理其未受管理的资源,则会发生内存泄漏.丑陋的东西.

保留调用Dispose()编译器的决心将消除这种清晰度,并使调试由非托管资源引起的内存泄漏变得更加困难.

  • "垃圾收集并不那么简单." - 为了记录,`IDisposable`与垃圾收集器无关.调用`Dispose()`以某种方式调用GC是一种常见的误解,但是`Dispose()`只是另一种方法,没有什么特别之处. (12认同)