15

What are your opinions on how disposable objects are implemented in .Net? And how do you solve the repetitiveness of implementing IDisposable classes?

I feel that IDisposable types are not the first-class citizens that they should've been. Too much is left to the mercy of the developer.

Specifically, I wonder if there should'nt have been better support in the languages and tools to make sure that disposable things are both implemented correctly and properly disposed of.

In C# for instance, what if my class that needs to implement the disposable semantics could be declared like this:

public class disposable MyDisposableThing
{
    ~MyDisposableThing()
    {
        // Dispose managed resources
    }
}

The compiler could in this case easily generate an implementation of the IDisposable interface. The destructor ~MyDisposableThing could be transformed into the actual Dispose method that should release managed resources.

The intermediate C# code would look like this:

public class MyDisposableThing : IDisposable
{
    private void MyDisposableThingDestructor()
    {
        // Dispose my managed resources
    }

    ~MyDisposableThing()
    {
        DisposeMe(false);
    }

    public void Dispose()
    {
        DisposeMe(true);
        GC.SuppressFinalize(this);
    }

    private bool _disposed;
    private void DisposeMe(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                // Call the userdefined "destructor" 
                MyDisposableThingDestructor();
            }
        }
        _disposed = true;
    }
}

This would make for much cleaner code, less boilerplate disposing code, and a consistent way of disposing managed resources. Implementing IDisposable by hand would still be supported for edge cases and unmanaged resources.

Ensuring that instances are properly disposed is another challenge. Consider the following code:

private string ReadFile(string filename)
{
    var reader = new StreamReader();
    return reader.ReadToEnd(filename);
}

The reader variable never outlives the scope of the method but would have to wait for the GC to dispose it. In this case, the compiler could raise an error that the StreamReader object was not explicitly disposed. This error would prompt the developer to wrap it in a using statement:

private string ReadFile(string filename)
{
    using (var reader = new StreamReader())
    {
        return reader.ReadToEnd(filename);
    }
}
Peter Lillevold
  • 33,668
  • 7
  • 97
  • 131
  • 3
    well, the first two sentences are... – Peter Lillevold May 28 '09 at 00:24
  • 1
    perhaps too much... guess I just needed to get my frustrations out :) – Peter Lillevold May 28 '09 at 00:25
  • 1
    Its comunity wiki...so why does it matter if its a question or not? Wiki is knowledge/informational, etc. – jrista May 28 '09 at 00:29
  • 1
    yeah this is a question. the question being, what does everyone think? do they agree? are there better ways? – andy May 28 '09 at 00:30
  • 1
    @jrista: good point, only noticed after you posted your comment – andy May 28 '09 at 00:31
  • Yeah, this question is fine in my opinion (given that it's wiki). It's certainly subjective, but the first paragraph states that clearly. – Noldorin May 28 '09 at 00:33
  • You can't say reader never outlives the method scope. Well, in this case you might, because I doubt a string will hold a reference to a `StreamReader`. But what if you call another method on reader that method's return value holds a reference to reader? – R. Martinho Fernandes Dec 29 '09 at 17:54
  • @Martinho - I'm referring to the specific code sample. But yes, in general, an instance can of course outlive the method it is instantiated in, either by returning it to the caller or storing a reference to the instance in some state outside the method. My point is that in the case of the sample, a compiler can easily, trough static analysis, detect the obvious missing call to Dispose and thus either demand an explicit call to Dispose or wrap the code in a using statement. – Peter Lillevold Dec 29 '09 at 20:08
  • Sometimes I think about writing small code generators (in PowerShell) to do stuff like this. It's tricky to get it right. I don't want to create a new external DSL if I can avoid it, as C# is a great language with great tools already. – Jay Bazuzi Jan 31 '10 at 18:47
  • I am hopeful that a future version of the C# compiler will provide extensibility points that would be useful for these kinds of transformations. – Jay Bazuzi Jan 31 '10 at 18:50
  • One function that your boilerplate Dispose code is missing is DEBUG-time verifications that Dispose was called. Start reading at http://blogs.msdn.com/ericgu/archive/2004/03/24/95743.aspx, another Eric that worked on C# at Microsoft. – Jay Bazuzi Jan 31 '10 at 18:50
  • @Jay - that made an interesting read! My code here is for illustration only. Production code would be made far more robust indeed. – Peter Lillevold Jan 31 '10 at 19:40

7 Answers7

24

An oft-stated principle is that "design patterns are needed to address language deficiencies". This is an example of that principle. We need the disposable pattern because the language doesn't give it to you.

I agree that disposability could have been elevated out of the "pattern" world and into the C# language proper, as we did with, say, property getters and setters (which are standardizations of the pattern of having getter and setter methods), or events (which standardize the idea of storing a delegate and calling it when something interesting happens.)

But language design is expensive and there is a finite amount of effort that can be applied to it. Thus we try to find the most useful, compelling patterns to put into the language proper. And we try to find a way that does so in a way that is not merely convenient, but actually adds more expressive power to the language. LINQ, for example, moves the concepts of filtering, projecting, joining, grouping and ordering data into the language proper, which adds a lot of expressive power to the language.

Though this is certainly a good idea, I don't think it meets the bar. It would be a nice convenience, I agree, but it doesn't enable any really rich new scenarios.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 7
    Again, the example of autoproperties are IMO mere convenience. Yet it is useful in that it solves the problem of inconvenience and code readability. Elevating the disposable pattern would not only solve inconvenience and code clutter, it would also help developers do the right thing. Certainly, it will not bring any new rich scenarios to the table, it will make code more expressive and safer which is at least a valuable scenario. – Peter Lillevold May 28 '09 at 07:20
  • 4
    Indeed, autoprops are pretty much just a convenience. I was quite astonished that they made it in to C# 3.0. The design, implementation, testing and documentation costs of autoprops turned out to be low enough to justify it, but still, it was surprising. – Eric Lippert May 28 '09 at 15:33
  • The benefit of elevating disposability out the the pattern and in to the language proper is that it allows the pattern to be implemented correctly. This is the single biggest problem I see with implementations of the dispose pattern...people think that by implementing IDisposable they are implementing the pattern, which isn't really the case. It also has the potential for allowing other patterns to be used that make use of the using semantics, which are very valuable but don't always have anything to do with disposability. – Scott Dorman Aug 30 '09 at 21:44
  • 6
    I am surprised Eric is surprised about autoprops :) The difference that they make is that it's actually easy enough to implement properties now (instead of fields) that even lazy programmers will use them where they should be used. That is a MASSIVE difference, which IMO is worth all that implementation effort. This proposal would make lazy programmers write properly disposable classes and properly dispose of them. – Roman Starkov Oct 06 '09 at 11:57
  • 1
    Oh, I agree that they are a lovely feature and totally worth it. But for scheduling C# 3 we were strongly devoted to the proposition that anything which might slip LINQ would be aggressively cut. Auto props and partial methods just barely squeaked in there. – Eric Lippert Oct 06 '09 at 16:37
  • It would be interesting if the C# compiler had an extensibility feature, with compile time hooks where user code can participate, like in Boo or Nemerle. This way, the language spec would be complete, and all the new "features" would be developed as compiler extensions, either by Microsoft or third parties. Patterns could be elevated to the language level. But, this might be really hard to achieve properly, and the tooling support would have to evolve as well: if I create a new extension "keyword", like `disposable`, then I want Visual Studio to also know about it. – Jordão Mar 26 '10 at 16:56
  • Seriously, I don't understand why not have such a handy feature. Reducing code bloat is something nobody would complain about. If the C# team can't be bothered to implement such a thing, why not provide extensibility? That way I could fix what I see as a problem for myself and no more costs on Microsoft's side. – Camilo Martin Nov 22 '10 at 23:32
  • 2
    @Camilo: Adding language complexity is something that *many* people complain about. We get many, many complaints every time we add a new feature because every new feature means more money spent training people how to use the new feature. Adding extensibility via metaprogramming compounds the problem further; now you have to train people not just how to read and write C#, but how to read and write C# with metaprogramming in it. That said, I am very interested in metaprogramming and would consider adding some form of it to a hypothetical future version of C#. – Eric Lippert Nov 23 '10 at 00:48
  • @Camilo: As for costs, your argument is spurious. You're saying that you want a free car, and when none is forthcoming, saying that you'll take a free factory and build the car yourself to save us money on not having to build the car. That's not actually a savings for us. Don't get me wrong; I'd love to give you that factory. I make tools for a living and I want to make the best tools possible. But we only have so many features we can do in any given release. – Eric Lippert Nov 23 '10 at 00:54
  • @Eric I understand the argument about having to train people on language extensions would add complexity, but I'd really love to have this in a C# 5. ;) And auto-implemented properties still aren't really the kind of thing one would need "training" for... I mean, when you look at them for the first time they actually make sense. Besides, it saves an awful lot of boilerplate on everyday code. – Camilo Martin Nov 23 '10 at 01:29
  • 1
    On a side note, I've learned C# just by looking at MSDN articles. And every new feature I've discovered while fiddling with the code was worth it, even syntactic sugar like an extension method was very worth it (actually it's not just syntactic sugar when I had to add a method to an enum: http://stackoverflow.com/questions/4030136/is-an-extension-method-the-only-way-to-add-a-function-to-an-enum ). I love when the code looks good, even if it takes learning something new. But then again, I do understand many people wouldn't like to find new features that require learning on their part. – Camilo Martin Nov 23 '10 at 01:39
  • @Eric: It's really great to always start the day reading your insights, better than coffee. Even though I don't drink any ;) – Joan Venge Nov 23 '10 at 18:00
6

In addition to the other answers, there is the problem of how much should this implement and what should people expect from it? Say I declared my class like this:

public disposable class MyClass
{
    readonly AnotherDisposableObject resource = new AnotherDisposableObject();

    ~MyClass()
    {
        this.resource.Dispose();
    }

    public void DoStuff()
    {
        this.resource.SomeMethod();
    }
}

Then what would you expect to happen if a caller called DoStuff after the instance had been disposed? Should the compiler automatically insert something like

if (this.disposed) throw new ObjectDisposedException();

at the start of every method because you have declared the class as disposable?

If so then what about cases where methods are explicitly allowed to be called after an object is disposed (e.g. MemoryStream.GetBuffer)? Would you have to introduce a new keyword that indicated this to the compiler, e.g. public useafterdispose void ...?

If not then how do you explain to people that the new keyword implements some of the boiler-plate code for you, but that they still need to write code to check whether the object is disposed in each method? Moreover, how can they even check this, because all the state information about whether the object has been disposed is auto-generated! Now they need to track their own flag in the ~MyClass method which undoes half the work the compiler should be doing for you.

I think as a specific language pattern there are too many holes in the concept, and it only attempts to solve one specific problem. Now what could solve this entire class of problem in a general-purpose fashion is mixins (i.e. a Disposable mixin) and this language feature would be generally reusable for different concepts (e.g. Equatable mixin, Comparable mixin, etc.). That's where my money would go.

Greg Beech
  • 133,383
  • 43
  • 204
  • 250
  • Valid points here. Having automatic generation of guards is not feasible imo, since the compiler would then have to "know" about which variables should be guarded. I can see that getting overly complicated and too much magic going on. The disposable keyword should generate an IsDisposed property though so that it would be easy to create guards. And yes, this should be part of the deal so that users wont have to track the disposed state themselves. – Peter Lillevold Jun 05 '09 at 08:57
5

Personally, I consider the support for IDisposable to be quite decent in the current version of .NET. The presence of the using keyword pretty much makes it into a first-class construct for me.

I do admit there is a certain amount of boilerplate code involved, but not enough to warrant a new language features. (Auto-implemented properties was a good example of a feature that was begging to be introduced.) You've missed out an important point in your post that this "boilerplate" code is not always what you need. Mainly, you need to dispose unmanaged resources outside of the if (disposing) block.

Of course, the destructor (~MyDisposableThing) and parameterless Dispose() method are genuinely boilerplate and could be eliminated by the user of a language keyword, as you suggest - but again I'm not sure the introduction of an actual new keyword is all that necessary for a few lines of code.

I certainly see the point you are making here, and do sympathise with it to some degree. (I'm sure no coder would complain if your suggestion becamse part of the language specification.) However, it's not likely to convince the .NET development team when there are a rather limited number of lines of code anyway, some of which are arguably fairly context-specific (and thus not boilerplate).

Noldorin
  • 144,213
  • 56
  • 264
  • 302
  • I state that "IDisposable by hand...for unmanaged resources", though I would like to see this scenario also supported in a more declarative way. Perhaps through a ~~MyDisposableThing destructor :) – Peter Lillevold May 28 '09 at 07:00
  • Arguably, autoproperties removed more boilerplate code when you have a lot of properties, but still only three lines of code per property. The dispose pattern requires much more code per class. So in cases where you have many classes that requires IDisposable the impact of a new keyword would be quite large. – Peter Lillevold May 28 '09 at 07:05
4

I completely agree that IDisposable needs better language suppprt. Here's my variant of it from a while ago. The details are probably wrong, but C++/CLI serves as a pretty good model for this. Unfortunately it confuses the hell out of C# programmers when I show them examples in C++/CLI. But it already does "the right thing" in terms of implementation; we would just need a new syntax in C#.

Even the simplest Windows Forms application has a Dispose method in it, which is generated by a wizard and is fragile in the face of inexpert changes. The idea of composing components together such that one component can "own" several others is so fundamental that IDisposable is practically unavoidable, and unfortunately it seems to take several pages of most books to explain how to implement it correctly.

The existing using statement takes care of the client side. Where we need more language support is on the implementation side.

Some of the fields of a class are references to things that the class "owns", and some not owned. So we have to be able to mark a field as owned.

Also, it would be a very bad idea to automatically generate a finalizer. Most often, a class will own other objects that implement IDisposable. Not all classes are thread safe, nor should they need to be. If they are called from a finalizer, that happens on another thread, forcing them to be thread safe. This is probably the one area around IDisposable that causes the most confusion - a lot of people read the books and come away with the mistaken impression that you have to write a finalizer on an object that supports IDisposable.

Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
  • I like your proposal, though I would add support support for wrapping constructors in a try-finally block (a flag would be set at the end of the 'try' indicating successful completion). Presently it's not possible to trap exceptions that occur before the "user code" in a constructor starts, or after it ends, even though exceptions could occur during those times (field initialization or derived-class construction). – supercat Jan 30 '11 at 04:49
2

I realize this is an old thread but there is something that has been overlooked.

Dispose pattern in both C# and Java break the fundamental reason for not having a deterministic destructor.

MyObject A = new MyObject()
MyObject B = A;
A.Dispose();

What is the state of B now? What if the owner of B didn't really want it disposed. You now have the same issue in C++ where you have to keep track of all the references of objects you are holding on to.

IDisposable is only truly valid within the context of using() and ensuring resource cleanup if an exception occurs.

There are design patterns to do this but it isn't IDisposable

@Peter Yes I am arguing that the Dipsosable pattern has a fault. When implemented the Disposable pattern isn't normally meant to just dispose OS resources with the idea of being able to continue using the object that was disposed. By using the Disposable pattern outside of a try{} finally{} in Java or using() in .NET you break one of the reasons of having a GC. I am not saying memory will leak. I am saying you can now have other parts of the code that have a reference to an Object that has been disposed. Now the onus is back on the developer to check if an object has been disposed before each call, or at the very least catch a ObjectDisposedException.

Lets look at a silly example:

FileStream stream = new FileStream (@"c:\mylog.txt");
logger.setStream (stream);

Who is supposed to invoke .Dispose()? It may not be obvious that the logger is now aquiring ownership of the file stream. Let's say the stream was created somewhere else outside of the developer knowing it will be set as the logging stream.

if we were to add one line, we would break the logger

using (FileStream stream = new FileStream (@"c:\mylog.txt"))
    { logger.setStream (stream); }

or

FileStream stream = new FileStream (@"c:\mylog.txt");
logger.setStream (stream);
stream.Dispose();

The Disposable pattern does not reference count for resources. The developer now has to be conscious of who owns the object and who is responsible for cleaning it up. The real problem is that when Dispose() is invoked the normal behavior is to invalidate the whole object preventing it from being used.

Andrew T Finnell
  • 13,417
  • 3
  • 33
  • 49
  • First, the GC is already doing reference counting for us, it must do this in order to know when objects can be collected. The problem shows up when non-managed data is held in which the GC cannot know when it is appropriate to release them. Hence the need for the special IDisposable. The argument in thus thread is not whether we need this interface or not (clearly we do). The question is could we benefit from having language support in order to implement the IDisposable interface. With this in mind, your answer seems a bit off: are you arguing that the disposable pattern is somehow at fault? – Peter Lillevold Feb 18 '11 at 19:23
  • The problem you note with `SetStream` has a simple solution: add a parameter to `SetStream` which explicitly indicates whether the stream should take ownership of the passed-in object or not. Note that if a call to `SetStream` gives it ownership of the object, the next call should call `Dispose` on *that* object regardless of whether it's being given ownership of the new one. – supercat Aug 03 '12 at 23:02
  • The extremely vast majority of `IDisposable`-related problems can be solved if one recognizes that at any given time an `IDisposable` object should have precisely one "owner"; other references may exist, but they should be regarded semantically not as holding on object, but merely identifying an object held by someone else. – supercat Aug 03 '12 at 23:04
  • @supercat You "solution" is what exactly C++ programmers use, not only for expensive system resources, but also for memory allocation. However we all see C++ programmers **MUST** pay a lot of attention to prevent ownership management faults. This is what this answer writer tried to figure out. With C++11 `shared_ptr` and deterministic distructors I believe this issue was (mostly) resolved . However I think it is still a pain in C# or Java. – Earth Engine Jul 21 '14 at 03:18
  • @EarthEngine: In general, mutable state requires ownership (whether or not it requires resources); immutable state does not. The only time tracking ownership of resources poses problems is when a resource encapsulating immutable state is shared, but ties up fungible resources that could otherwise be used for other purposes (e.g. an immutable bitmap stored in a display-card cache). Otherwise, the best model is for things which encapsulate resources to implement RAII and things which don't encapsulate resources to use GC. I wish system designers would solidly integrate both. – supercat Jul 21 '14 at 15:57
0

IMHO, the .net languages have a major shortcoming in their handling of iDisposable, which is that there is no nice way of handling initializers that throw exceptions. Unless one 'leaks' a copy of the object under construction or the disposable objects therein, there's no way to clean up any iDisposable objects which were created (either in an initializer, or a base-level constructor) before an initializer throws.

Two features I would like to see toward that end:

  1. A class declaration that would cause a particular method to be invoked if an exception throws out of its constructor.
  2. A field declaration which would indicate that the field should have .Dispose called upon it if some special private method or keyword is used on the object.

BTW, I would also like to see a declaration available for structure methods which would indicate that the method alters the underlying structure. Use of such methods would be forbidden on structure rvalues, and use of such methods on structure properties would generate a read-modify-write sequence.

supercat
  • 77,689
  • 9
  • 166
  • 211
-1

Okay you need to understand the difference between managed and unmanaged memory. Put simply, c++ style destructors wouldn't work in the managed world of c# because there's no guarantee when the object gets garbage collected, hence you would never know when the destructor would get called, which would make things very unpredictable.

As opposed to c++, when destructors get called as soon as the class goes out of scope, so you can guarantee when it gets called.

This is the reason why c# can't have destructors.

Chris
  • 39,719
  • 45
  • 189
  • 235
  • I don't think that's what the OP is trying to say. He's simply making the point about the level of boilerplate required. IDisposable classes would clearly be demarcated using a keyword (and thus treated differently from ordinary classes) in his example. – Noldorin May 28 '09 at 00:35
  • @Chris: That's why the topic is on the Dispose method, which does have precise execution time. – Jimmy May 28 '09 at 00:38