开发者

returning a memoryStream as a string

开发者 https://www.devze.com 2022-12-24 13:53 出处:网络
What is wrong with this statement? return Encoding.ASCII.GetString(memoryStream.GetBuffer(), 0, memoryStream.Length)

What is wrong with this statement?

return Encoding.ASCII.GetString(memoryStream.GetBuffer(), 0, memoryStream.Length)

I know about the Dispose pattern, have checked out the underlying memoryStream and seen that there is nothing really happening in the di开发者_如何学Pythonspose. So why shouldn't I allow one of my developers to do this.

The memoryStream becomes out of scope after the return.

The aim is to make the code succinct and not create references that are not required, Hopefully getting the Garbage Collector to kick in soon as.

Its just niggling me, I feel that the memoryStream lets the party down, when compared to what the other streams do and why they implement the IDispose.

Can someone please give me a good reason not to allow the code above. I got a gut feeing about the code not being right but need some back up. :)

****WeNeedAnswers**** :edit please note the code has changed due to Jon Skeets input, but this questions main core is still relevant.

My original and error prone code was:

return new ASCIIEncoding().GetString(memoryStream.ToArray());


Personally I'd use:

string text;
memoryStream.Position = 0;
using (TextReader reader = new StreamReader(memoryStream, Encoding.ASCII))
{
    text = reader.ReadToEnd();
}

as a more general way of doing it - but your code should at least work. If you're aiming not to create more objects than you need, you should use Encoding.ASCII instead of creating a new instance. If you're really paranoid about copying, you could use:

string text = Encoding.ASCII.GetString(memoryStream.GetBuffer(),
                                       0, memoryStream.Length);

This would avoid creating a copy of the data.

You seem to be concerned about memory usage - do you have any reason for this? Have you performed profiling and found that garbage collection is a bottleneck? If not, don't worry about it until you do find it to be a problem. It usually isn't.


"Hopefully getting the Garbage Collector to kick in soon as."

I'm going to give a short and over-simplified GC primer, because you don't seem to appreciate how it works...

First off, the GC isn't deterministic. It doesn't kick in "soon as"; it generally kicks in when there's memory pressure. So regardless of what you do in your method, you can't guarantee that it'll run at the end of your method, or at any other specific time. Typically, a GC will run when there's not enough space in GC Generation 0 to allocate a new object.

Secondly, if you don't call Dispose on your MemoryStream (either manually or with a using block) your GC won't release that memory, even if it runs. When you call a typical Dispose, two things happen: first, the object is tidied up, and second a call is made to GC.SuppressFinalize. The latter call is important: it tells the GC that you've already done cleanup for the object.

If haven't called Dispose on your object, then the GC will put your object on a "Finalizer queue." This is a queue of objects waiting to be tidied up. If your object ends up on this queue, it can't be released, because the queue is keeping it alive. That means your object gets pushed into GC generation 1 and won't get cleared up for a lot longer. GC generation 1 won't get collected until generation 0 has been collected and the objects that can't be released don't fit in Gen 1. For most apps the ratio is approximately 10 GC gen-0 collects for one GC gen-1 collect, so you're prolonging the life of your object by a factor of 10 by not calling Dispose.

Moral of the story: if a type implements IDisposable, and you're worried about memory use (or even just about good programming practise), call the Dispose method. It's there for a reason.

Thirdly: Even if you call Dispose sensibly, the memory release still isn't deterministic, because all you do ensure that the next GC will release the memory.

Finally: Don't be tempted to call GC.Collect yourself. This is almost always a bad thing, and will typically result in your application having a higher memory footprint, not a lower one.


The MemoryStream implements IDisposable because it inherits from the more general Stream class. But there is really no need to call Dispose() on this.

The point with the Dispose() function is to tell the class to clean up any unmanaged resources that it might have acquired. For a FileStream that would be file handles and file locks. But the MemoryStream only allocates memory, and therefore it is not necessary to dispose it. When your memory stream is out of scope, it is ready for garbage collection, no matter if you call Dispose() or not.

But normally, you should always call Dispose() on objects that implements IDisposable, and allowing exceptions may create confusion among developers who do not have a thorough understanding of what is going on.

I think some of your concerns come from a poor understanding of the .NET memory and garbage collection model. I learned from the book Applied Microsoft .NET Framework Programming. It is an ancient book, targeting .NET 1.0, but the section on memory management and garbage collection is really worth the read.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号