问题
When a variable is IDisposable, we have the using
keyword to manage the disposal. But what if we return the value in a method, should we have using
twice?
StringContent stringToStringContent(string str)
{
using (StringContent content = new StringContent(str))
{
return content;
}
}
void logStringContent()
{
using (StringContent content = stringToStringContent("test"))
{
Debug.WriteLine(content.ToString());
return;
}
}
In this example above, I only have 1 new
but I have 2 using
for the same thing. So I feel it's unbalanced. Is it better to:
a) keep both using
, and the language/compiler knows its job to avoid double-disposal?
b) keep only using
with new
together, and no need in other cases?:
void logStringContent()
{
StringContent content = stringToStringContent("test");
Debug.WriteLine(content.ToString());
return;
}
c) keep only using
when you don't return, and no need when you return?:
StringContent stringToStringContent(string str)
{
return new StringContent(str);
}
The only thing I can feel is that b) isn't the correct answer, because it wouldn't work for issues like the one described here: .NET HttpClient hangs after several requests (unless Fiddler is active)
回答1:
I think c
is the right answer here - you're returning (a reference to) an object from the method - it makes no sense to have already disposed of that object before you return it. For example, File.OpenRead
wouldn't dispose of the stream that it returns, would it?
It would be a good idea to indicate in the method documentation that the caller takes responsibility for disposing of the object though. Likewise, some methods accept a disposable type and state that the caller then shouldn't dispose of the object themselves. In both case there's effectively a transfer of responsibility for disposing of the object properly.
回答2:
Having methods that return IDisposable
objects is not an uncommon pattern, but preventing resource leaks when exceptions occur can be difficult. An alternative approach is to have a method which will produce a new IDisposable
accept an out
or (if the method is unsealed and virtual) ref
parameter, and store a reference to the new object in that. The caller would then be expected to Dispose
the thing in question, whether the method that produced it returned normally or threw an exception.
Otherwise, if you want your method's return value to be a new IDisposable
, and if any code will execute between the time your method acquires a resource and the time it returns, you should guard your code with something like:
DisposableThing thingToDispose = null;
try
{
thingToDispose = new DisposableThing(whatever);
// Now do stuff that might throw.
// Once you know you're going to return successfully...
DisposableThing thingToReturn = thingToDispose;
thingToDispose = null;
return thingToReturn;
}
finally
{
if (thingToDispose != null)
thingToDispose.Dispose();
}
Note that this code doesn't "catch" any exceptions, but if the function exits other than via the proper designated path the newly-constructed object will get disposed. Note that if this function throws an exception without disposing of the newly-constructed object, any resources acquired by that object will leak, since the caller won't be able to dispose it.
来源:https://stackoverflow.com/questions/16861329/best-practice-to-avoid-multiple-disposals-with-the-using-keyword-in-c-sharp