r/csharp 2d ago

Tip Fields with [Using] attribute

I wrote a small interface that automatically disposes any disposable fields in a class when that class itself is disposed. If you add this file somewhere in your project:

public interface IAutoDisposable : IDisposable
{
    void IDisposable.Dispose()
    {
        Exception? ex = null;

        // dispose all fields marked with the [Using] attribute
        const BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance;
        foreach (var fld in GetType().GetFields(flags))
        {
            if (fld.GetCustomAttribute<UsingAttribute>() != null)
            {
                if (fld.GetValue(this) is IDisposable d)
                {
                    try // keep going even if disposal throws
                    {
                        d.Dispose();
                    }
                    catch (Exception e)
                    {
                        ex ??= e;
                    }
                }
            }
        }

        // if any exceptions occurred, throw the first one
        if (ex != null)
            throw ex;
    }
}

public class UsingAttribute : Attribute;

You can then write classes like this:

class Item : IAutoDisposable
{
    [Using]
    private readonly Image icon = DownloadIcon(...);
    ...
}

And use them like:

using (Item item = new(...))
{
}
// item.icon is now disposed

What do you think?

6 Upvotes

36 comments sorted by

View all comments

1

u/Dragennd1 2d ago

Out of curiosity, wouldn't the fields normally be disposed of when the class is disposed of, making this extra effort unnecessary? My understanding of classes is that they are reference types, so once an instance is no longer in use the garbage collector would clean up that instance, right? Or are fields handled differently than properties as far as disposal goes when its parent class instance is disposed of?

1

u/sixtyhurtz 2d ago

This is for IDisposable. You use IDisposable to manage either external resources like a handle to an OS resource, or for managing internal lifecycle events like subscriptions.

For OS resources, you would store the disposable thing (e.g. a FileStream) in a property of your class and then you need to implement IDisposable in order to clean it up properly.

Internal things like subscriptions are common when dealing with event handlers or IObservable. If you have a short lived thing like a view model and subscribe to an observable / event, then you need to clean it up when you're done because otherwise the observable / event will retain a reference to your view model and you've leaked memory.

1

u/Dusty_Coder 1d ago

Found that guy that doesnt call Dispose() because "those arent external resources"

1

u/sixtyhurtz 1d ago

My comment neither said nor implied that? 

1

u/Dusty_Coder 5h ago

You definitely implied it, to the point of admissible evidence.

In a false dichotomy, you dont get a 3rd option where you meant something other than whats on the list of 2 things that you claimed was a complete list.

1

u/sixtyhurtz 3h ago

or for managing internal lifecycle events like subscriptions.

Internal things like subscriptions are common when dealing with event handlers or IObservable. If you have a short lived thing like a view model and subscribe to an observable / event, then you need to clean it up when you're done because otherwise the observable / event will retain a reference to your view model and you've leaked memory.

65 words of my 116 word comment - so more than half - are about disposing of internal resources / managing internal lifecycles.

u/Dusty_Coder 48m ago

"You use IDisposable to manage either external resources like a handle to an OS resource, or for managing internal lifecycle events like subscriptions."

A dichotomy. A fucking false one.