system.net.httpclient和system.net.httpclienthandler在。net Framework 4.5中实现了IDisposable(通过System.Net.Http.HttpMessageInvoker)。
using语句文档说:
作为规则,当使用IDisposable对象时,应该声明和
在using语句中实例化它。
这个答案使用了这个模式:
var baseAddress = new Uri("http://example.com");
var cookieContainer = new CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("foo", "bar"),
new KeyValuePair<string, string>("baz", "bazinga"),
});
cookieContainer.Add(baseAddress, new Cookie("CookieName", "cookie_value"));
var result = client.PostAsync("/test", content).Result;
result.EnsureSuccessStatusCode();
}
但是来自Microsoft的最明显的例子既没有显式调用也没有隐式调用Dispose()。例如:
最初的博客文章宣布HttpClient的发布。
HttpClient的实际MSDN文档。
BingTranslateSample
GoogleMapsSample
WorldBankSample
在公告的评论中,有人问微软员工:
在检查了你的样品后,我发现你没有进行处理
对HttpClient实例的操作。我已经使用了HttpClient的所有实例
在我的应用程序上使用声明,我认为这是正确的方式
因为HttpClient实现了IDisposable接口。我是在
正确的道路?
他的回答是:
一般来说,这是正确的,尽管你必须小心
"using"和async,因为它们在。net 4和。net 4.5中不能真正混合
可以在“using”语句中使用“await”。
顺便说一句,你可以重复使用同一个HttpClient,次数不限
通常情况下,您不会一直创建/处理它们。
第二段对这个问题来说是多余的,它不关心您可以使用HttpClient实例多少次,而是关心在您不再需要它之后是否有必要处理它。
(更新:事实上,第二段是答案的关键,如下文由@DPeden提供。)
所以我的问题是:
Is it necessary, given the current implementation (.NET Framework 4.5), to call Dispose() on HttpClient and HttpClientHandler instances? Clarification: by "necessary" I mean if there are any negative consequences for not disposing, such as resource leakage or data corruption risks.
If it's not necessary, would it be a "good practice" anyway, since they implement IDisposable?
If it's necessary (or recommended), is this code mentioned above implementing it safely (for .NET Framework 4.5)?
If these classes don't require calling Dispose(), why were they implemented as IDisposable?
If they require, or if it's a recommended practice, are the Microsoft examples misleading or unsafe?
简短的回答:不,目前接受的答案中的陈述是不准确的:“普遍的共识是您不(不应该)需要处理HttpClient”。
长话短说:以下两种说法都是正确的,同时也是可以实现的:
引用自官方文档的“HttpClient旨在被实例化一次,并在应用程序的整个生命周期中被重用”。
一个IDisposable对象被假定/建议被释放。
而且它们彼此之间并不必然冲突。这只是你如何组织你的代码来重用HttpClient并且仍然正确地处理它的问题。
从我的另一个回答中引用了一个更长的回答:
见到人不是巧合
在一些博客文章中指责HttpClient的IDisposable接口
使他们倾向于使用using (var client = new HttpClient()){…}模式
然后导致套接字处理程序耗尽的问题。
我认为这可以归结为一个未说出口的(错误的)概念:
“一个IDisposable对象预期是短命的”。
然而,当我们以这种风格编写代码时,它确实看起来像一个短命的东西:
using (var foo = new SomeDisposableObject())
{
...
}
IDisposable的官方文件
从来没有提到IDisposable对象必须是短命的。
根据定义,IDisposable只是一种允许您释放非托管资源的机制。
仅此而已。从这个意义上说,你最终会触发处置,
但它并不要求你在短时间内这样做。
因此,正确选择何时触发处置是你的工作,
基于真实对象的生命周期需求。
没有什么能阻止你长久地使用一次性卫生纸:
using System;
namespace HelloWorld
{
class Hello
{
static void Main()
{
Console.WriteLine("Hello World!");
using (var client = new HttpClient())
{
for (...) { ... } // A really long loop
// Or you may even somehow start a daemon here
}
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
}
With this new understanding, now we revisit that blog post,
we can clearly notice that the "fix" initializes HttpClient once but never dispose it,
that is why we can see from its netstat output that,
the connection remains at ESTABLISHED state which means it has NOT been properly closed.
If it were closed, its state would be in TIME_WAIT instead.
In practice, it is not a big deal to leak only one connection open after your entire program ends,
and the blog poster still see a performance gain after the fix;
but still, it is conceptually incorrect to blame IDisposable and choose to NOT dispose it.
简短的回答:不,目前接受的答案中的陈述是不准确的:“普遍的共识是您不(不应该)需要处理HttpClient”。
长话短说:以下两种说法都是正确的,同时也是可以实现的:
引用自官方文档的“HttpClient旨在被实例化一次,并在应用程序的整个生命周期中被重用”。
一个IDisposable对象被假定/建议被释放。
而且它们彼此之间并不必然冲突。这只是你如何组织你的代码来重用HttpClient并且仍然正确地处理它的问题。
从我的另一个回答中引用了一个更长的回答:
见到人不是巧合
在一些博客文章中指责HttpClient的IDisposable接口
使他们倾向于使用using (var client = new HttpClient()){…}模式
然后导致套接字处理程序耗尽的问题。
我认为这可以归结为一个未说出口的(错误的)概念:
“一个IDisposable对象预期是短命的”。
然而,当我们以这种风格编写代码时,它确实看起来像一个短命的东西:
using (var foo = new SomeDisposableObject())
{
...
}
IDisposable的官方文件
从来没有提到IDisposable对象必须是短命的。
根据定义,IDisposable只是一种允许您释放非托管资源的机制。
仅此而已。从这个意义上说,你最终会触发处置,
但它并不要求你在短时间内这样做。
因此,正确选择何时触发处置是你的工作,
基于真实对象的生命周期需求。
没有什么能阻止你长久地使用一次性卫生纸:
using System;
namespace HelloWorld
{
class Hello
{
static void Main()
{
Console.WriteLine("Hello World!");
using (var client = new HttpClient())
{
for (...) { ... } // A really long loop
// Or you may even somehow start a daemon here
}
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
}
With this new understanding, now we revisit that blog post,
we can clearly notice that the "fix" initializes HttpClient once but never dispose it,
that is why we can see from its netstat output that,
the connection remains at ESTABLISHED state which means it has NOT been properly closed.
If it were closed, its state would be in TIME_WAIT instead.
In practice, it is not a big deal to leak only one connection open after your entire program ends,
and the blog poster still see a performance gain after the fix;
but still, it is conceptually incorrect to blame IDisposable and choose to NOT dispose it.