我需要关闭.NET服务引用客户端吗?

20
我想了解在使用完.NET服务引用客户端后是否有必要关闭它。几乎所有我在网上找到的示例似乎都没有关闭,但生成的客户端实现了IDisposable,因为它确实打开了与服务的连接,所以我的直觉告诉我在完成后需要关闭该连接。
这是我从 http://msdn.microsoft.com/en-us/library/bb386386(v=VS.90).aspx 中获取的代码示例:
private void button1_Click(System.Object sender, System.EventArgs e)
{
    ServiceReference1.Service1Client client = new 
        ServiceReference1.Service1Client();
    string returnString;

    returnString = client.GetData(textBox1.Text);
    label1.Text = returnString;
}

我认为你至少应该在此方法的结束处调用client.Close(),最好还应将第一行包装在using语句中。我只是想获取一些反馈,以了解最佳实践是什么。

3个回答

26

是的,你需要关闭实现了ICommunicationObject的任何内容,但在这样做时需要非常小心。如果通道发生错误或故障,可能导致对象的处理时间过长。

因此,建议您先调用Close方法,然后对IDisposable调用Dispose方法,使用一些catch语句处理特定的异常类型,并在最后调用Abort再调用Dispose

你可以将这个逻辑封装在一个实现了IDisposable接口的类中,然后在using语句中使用它。

关键在于创建一个实现了IDisposable的标记,在该实现中调用Close,捕获相关异常,调用Abort(如果必要),然后调用Dispose

这个实现作为一个扩展方法实现,返回一个IDisposable,从而允许您在using语句中使用它。


4

最佳实践是,如果类实现了IDisposable接口,在finally语句块中调用Dispose()方法,或者使用using () { }语句块包装。

编辑
根据下面@casperOne的评论,似乎应该更加谨慎地处理WCF客户端。我不知道这一点,并且对此感到有些不安,因为使用using迄今为止一直很好用。


1
我很困惑为什么我找到的所有MSDN示例都没有这样做。他们不想在给出的示例中说明最佳实践吗? - nitramssirc
希望如此,但我认为这些示例通常演示其他类的特定点。我曾经看到过加密示例,它对文件进行编码,甚至没有在FileStream实例周围尝试/捕获,我们永远不会这样做,对吧?(!) - Neil Moss
1
-1:实现ICommunicationObject接口的对象不能简单地调用Dispose,原因在这里指出:http://caspershouse.com/post/Using-IDisposable-on-WCF-Proxies-(or-any-ICommunicationObject-implementation).aspx和这里:http://msdn.microsoft.com/en-us/library/aa355056.aspx - 在面对某些异常时必须中止操作,然后再调用Dispose。 - casperOne
4
这应该被视为ICommunicationObject实现中的一个漏洞。我们什么时候开始容忍未预期的using语句结果了呢? - slf
1
@slf:自从2006年WCF发布以来,我们一直忍受着它。这是一个设计缺陷,而不是一个错误。 - John Saunders

-2

最好的方法是查看Dispose()生成的客户端代码,并查看是否真正处置了任何东西,例如HTTP连接或其他东西。

一方面,它可能只是实现的接口从IDisposable继承,因为一些客户端可能需要处理某些内容,即使该特定的接口不需要。这类似于MemoryStream,它实现IDisposable,因为所有Stream都要这样做,但实际上并不涉及任何非托管资源。

另一方面,即使Dispose()是一个空方法,使用using也没有坏处。微软的示例实际上非常糟糕,甚至如果应该使用using也不使用(例如here),因此不要将它们的示例作为您不需要的良好证据。


我个人宁愿不去反向工程第三方程序集,以查看是否可以避免调用Dispose - 这难道不会引入风险吗?在下一个版本的组件或框架中,您可能需要处理并且留下一个代码库,将非托管资源清理交给GC吗? - Neil Moss
2
-1:编码时只针对实现而不是契约,这总是一个坏主意。如果类型实现了IDisposable接口,那么Dispose方法应该始终被调用。唯一不调用的原因是确定你有一个特殊情况,并且这样做会损害你的系统并需要一个解决方法。此外,在你的MemoryStream示例中,你应该调用Dispose方法,因为不能保证未来不会使用非托管内存作为后备存储,所以如果这方面发生变化,你的代码就无法适应未来。 - casperOne
@neil,如果你有实际的代码,请务必调用Dispose();我建议这样做。@casperOne,编写接口通常是一个好主意,但作为一条不可变的规则,它忽略了类型系统并不完美的事实。例如,非托管内存部分是一个红色的干扰项:我可以实现IList来使用非托管内存,但如果消费者编写到IList接口,他甚至不知道自己可以调用Dispose()。 - user24359
永远不要对像WCF这样的复杂系统进行反向工程并依赖它。特别是因为生成的客户端代码并不是实际执行工作的代码 - 实际上是WCF的一部分基础类在执行。 - John Saunders
你是在暗示阅读你生成的代码就等于逆向工程所有WCF吗?并不完全是这样。此外,对于已经非常强大的“总是”和“从不”的斜体使用过度强调了你的情况。OP问他是否需要在对象上调用Close,答案是——无论你引用多少工程准则——它取决于代码的内容。 - user24359
显示剩余2条评论

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