在C#中,使用[using]语句还是[dispose]方法更好?这适用于外部(COM)对象吗?

3

当使用完一个对象后,是使用using指令还是dispose指令更好呢?

 using(FileStream fileStream = new FileStream(
            "logs/myapp.log",
            FileMode.Open,
            FileAccess.Read,
            FileShare.ReadWrite))
        {
            using(StreamReader streamReader = new StreamReader(fileStream))
            {
                this.textBoxLogs.Text = streamReader.ReadToEnd();
            }
        }

另一方面,当我处理System.Net.Mail时,我被告知需要Dispose()对象以释放任何残留的锁定。

是否有任何一致性的指导?如何确定在特定情况下对于给定对象什么更为合适?


1
CDO已经过时得太离谱了。微软唯一能让人们停止使用它的办法就是放弃对其提供任何支持。在这方面,你只能自己解决,我想。 - Hans Passant
@Hans Passant - 尽管如此,.NET Framework在底层使用COM。System.Web.Mail实际上在内部使用了CDO(Collaboration Data Objects),就我所知。也许在我每天使用的对象中还隐藏着其他类似的COM对象。 - makerofthings7
1
用一个过时的组件来替换另一个过时的组件是没有意义的。请使用System.Net.Mail。 - Hans Passant
3
我只是想强调需要处理可能在 COM 下面的 .NET 对象,而不是讨论可支持性话题。在它仍受支持时,什么是最佳实践? - makerofthings7
1
请注意,当StreamReader被释放时,它会同时释放其基础流。在您的示例代码中,如果您在streamReaderusing块结束之前,但在fileStreamusing块结束之前使用fileStream,将会抛出一个ObjectDisposedException异常。 - Mashmagar
8个回答

9
使用语句(不是指令)在finally块中隐式调用Dispose()。因此,这里没有矛盾。你能链接到那个讨论吗? using (x) { ... }的官方定义是try ... finally if (x != null) x.Dispose(); }

哪个更好?

从符号学角度来看,using() { }块更好。从技术上讲,它们是相同的。

@Jesse:是的,你说得对。我认为我的代码是一种简化。 - H H
这次讨论是我在进行Microsoft Premier案例时收到的电子邮件线程的一部分,我无法转发它。该案例围绕使用CDO通过C#进行,并且虽然通过COM-Interop可以工作,但不受MSFT支持。 - makerofthings7
@制作者:那么我认为你的问题不是关于usingDispose(),而是关于何时/如何释放(清理)COM对象。 - H H
@Henk Holterman 使用/释放和 COM 清理之间有什么区别?我准备好学习了。 - makerofthings7
2
@MakerOfThings - COM对象不实现IDisposable,因为IDisposable是一个.NET接口,而COM不是.NET。如果您直接使用COM对象,请尝试使用Marshal.ReleaseComObject来销毁它。 - Joel Mueller
1
@MakerOfThings7:通常情况下,COM对象会被封装在C#对象中。这个封装器会在Dispose()方法中为您移除COM引用。 - user180326

6

这是同样的事情。使用很简单,如果你创建对象并且只在一个方法中使用它,则使用 using。如果你需要在方法调用之后保持对象生存,则必须使用 Dispose()。

COM对象的运行时可调用包装器没有Dispose()方法。


我需要为COM对象做什么特别的准备吗? - makerofthings7
这是一个很复杂的问题,有很多种情况。如果你只是添加了引用,那么不太可能会发生。最终处理器线程会负责它们。 - Hans Passant
2
@MakerOfThings7:你必须明确释放你的COM对象可能已获取的所有资源。具体如何做取决于你处理的COM对象。 - Eric J.
这不是 COM 的工作方式,对象负责资源管理。必须如此,你不可能猜到它使用了什么。 - Hans Passant

6

我想不出除了在另一个实现Dispose()的类(例如实现IDisposable的类)中时手动调用Dispose()的理由,此时可以将对象包装在using块中。使用块将对象的创建和处理放置在try/catch/finally块中,以基本上保证对象将被正确处理。

编译器比我或你更可靠。=)

MSDN记录了using语句并指出您可以获取C#语言规范,在其中您可以查看第8.13节“使用语句”(至少在v4.0参考中是8.13),该节详细解释了使用语句及其使用方法。第五段给出以下内容:

一个using语句被翻译成三个部分:获取、使用和处理。资源的使用隐式地包含在try语句中,该语句包括finally子句。这个finally子句会处理资源。如果获取到了null资源,则不会调用Dispose,也不会抛出异常。

1
这里有一个例子:在你自己类的Dispose()实现中处理私有成员对象。 - recursive
@递归,说得对,不知道怎么会漏掉那个。我想必是脑子短暂消失了!=)不过现在已经修复了。 - Rob
这是另一个问题,清除私有成员(或this),因为您的代码处于可以提前安全清理一切的位置,可能会提高效率,更重要的是保证安全性(如果调用代码未调用dispose),尽管外部看起来对象仍然“活动”。但仍然是内部使用,并不适用于using - Jon Hanna
另外还有几个释放资源的原因:一个工厂方法在创建了一个IDisposable实例后抛出异常,或者一个构造函数在创建了一个或多个IDisposable字段后抛出异常。如果工厂方法或构造函数本身没有进行Dispose操作,这些资源就不会被释放(在我看来,C#和vb.net中抛出异常的构造函数无法自动请求清理资源是一个不足之处)。 - supercat

3
using(foo) 
{
    foo.DoStuff();
}

只是语法糖,等同于以下写法:

try
{
    foo.DoStuff();
}
finally
{
    if(foo != null)
        foo.Dispose();
}

所以我不确定争论是从哪里来的。using块确实会调用dispose方法。大多数人都喜欢尽可能使用using块,因为它们更清晰、更简洁,易于理解正在发生什么。


1
不完全准确 - 你的foo.Dispose()实际上会被展开为((IDisposable)foo).Dispose(),用于处理显式实现IDisposable接口的情况。 - Jesse C. Slicer
@Jesse,啊是的,说得对。我忘记了显式实现。 - Matt Greer

3
只要对象的生命周期在代码块内,就使用using。如果您的对象需要长时间存活,例如在异步调用后被处理,您需要手动调用Disposeusing块比您记住所有可能和不可能的执行方式离开代码块来得更好。

2

在退出时使用Dispose调用。使用using更好,因为它确保调用dispose。


2

using块在执行完毕后会自动调用Dispose()


1

在任何可以的地方使用“using语句”有一个非常重要的原因。

如果通过using语句包装的代码抛出异常,你可以确保“using对象”会被处理。


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