20: C# Destructors and IDisposable.
Take Up Code - A podcast by Take Up Code: build your own computer games, apps, and robotics with podcasts and live classes
Categorie:
C# calls them finalizers and that’s strangely appropriate because all you really know about them is that they might eventually be called, finally. This is because C# decided to manage object lifetimes for you so you normally don’t need to worry about leaking memory anymore. This is called garbage collection. The problem with this is that you now have other things to worry about. C++ is notorious for memory leaks so the designers of C# decided to fix this by cleaning up any used memory for you. But memory is just part of what destructors help take care of. In order to control exactly when files get closed or other resources are returned, C# provides a method called Dispose. The Dispose method is part of the Disposable interface. There are some gotchas to worry about with calling Dispose. This episode explains how you should call Dispose as part of a “using” statement. This will guarantee that Dispose will be called even if an exception is thrown. Listen to the full episode or read the full transcript below. Transcript C# is a great language that offers some big productivity gains. But like any engineering project, there’s always tradeoffs. C++ is notorious for memory leaks, so the designers of C# wanted to fix this. The solution was to let the runtime manage all memory allocations. In fact, you don’t even have access to pointers in C# unless you drop into unsafe mode. In C#, destructors are called whenever the runtime determines that the object instance is no longer being used. But the runtime uses its own schedule for this. Garbage collection brings up images of household trash bundled in trash bags sitting on the roadside curb waiting to be picked up. That’s not really how managed code works though. A better analogy would be for you to just leave your wrappers and trash laying around when you’re done. Finished eating that fast food burger? You don’t even need to crumple up the wrapper. Just let go of it and let it fall wherever. The managed code runtime will periodically walk through your house, reach into your car, and generally go places no curbside pickup service would ever be allowed. That’s great for memory, at least normally, but what about that other aspect of destructors. What happens when you open a file or need to commit your changes to a database. You still want, and really need, those actions to take place at specific times that you can count on. Only with C#, you can’t count on when your destructors will be run. Because the runtime has its own schedule, it can sometimes investigate an object and find that you’re legitimately still using it. So it’s not a candidate for destruction. In order to improve performance, the creators of C# decided that the runtime just couldn’t keep investigating every single object instance every time. So when it finds an object still being used, it moves that object to a new survivor’s list that will be checked at some future time to see if the object is still being used or not. If it’s still being used, then it gets moved to yet another list that gets investigated much less frequently. Each of these lists is called a generation. There’s generation zero, one, and two. Generation zero objects get investigated quickly and because of this C# is optimized for short object lifetimes. This means that C# works really well when you create an object, use it quickly, and then forget about it right after that. The problem though is that files, databases, maybe some elaborate calculations tend to create objects that pass through generation zero and will be looked at later. So the very things you need precise control over making sure that resources are released, are the things that you tend to have the most trouble with. It’s not all lost though. There is a solution that most C# developers learn about early on. What’s not always appreciated is the engineering tradeoff that went into th