0

Given the following extension method:

public void DisposeIfNotNull(this IDisposable disposableObject)
{
    if (disposableObject != null) {
        disposableObject.Dispose();
        disposableObject = null;
    }
}

Can anyone explain why the following line doesn't have any effect: disposableObject = null; ?

Nick N.
  • 12,902
  • 7
  • 57
  • 75
  • You're working with the local copy of the variable, you'd need to pass by `ref` which you can't do in an extension method. Make `DisposeIfNotNull` return your `disposableObject` and assign the value back to the caller. – DGibbs Sep 30 '14 at 11:45
  • 2
    What are you actually trying to achieve? You realise that setting an object to null has no affect on when the GC collects it? As such even if your method worked it is a slightly less useful version of `Dispose()` that you can't put in a `using` block. – Ben Robinson Sep 30 '14 at 11:53
  • 1
    I think it is probably good you cannot do what you are trying to! – Justin Harvey Sep 30 '14 at 11:53
  • 1
    @BenRobinson that is not the point, I was wondering how to explain that this particular thing doesn't work. This method was for shortening the null check only. – Nick N. Sep 30 '14 at 12:21
  • Fair enough if its a contrived example, I was just pointing out that your code did not seem to make much sense. – Ben Robinson Sep 30 '14 at 12:29
  • It doesn't have any effect because you don't use the variable between assigning it `null` and where it goes out of scope. Try an IDE, addon, compiler or static analyzer that shows you pointless code. – Tom Blodget Oct 01 '14 at 04:13
  • 1
    possible duplicate of [Can I null an object with its own extension method?](http://stackoverflow.com/questions/11922839/can-i-null-an-object-with-its-own-extension-method) – Tom Blodget Oct 01 '14 at 04:19
  • @TomBlodget this is not a duplicate, I asked why it can't be done, the other question just states that it can't, I think the answer here is better. – Nick N. Oct 01 '14 at 08:09
  • I don't entirely disagree that @noelicus's answer is a good one. But there are many questions in this area that don't differ substantially. The point of "close as a duplicate" is not to erase but to link. [Here](http://stackoverflow.com/q/2618597/2226988) is another you might find useful. – Tom Blodget Oct 01 '14 at 17:30
  • @TomBlodget very interesting indeed – Nick N. Oct 02 '14 at 09:54

2 Answers2

7

Because you are setting the local reference to null only - not the reference you're passing in.

If it wasn't an extension you could use the ref keyword, but that's not compatible with an extension parameter.

noelicus
  • 14,468
  • 3
  • 92
  • 111
0

To achieve something similar in a reusable fashion, you can create a static helper method:

public static class Disposable
{
    public static void DisposeIfNotNull(ref IDisposable disposableObject)
    {
        if (disposableObject != null)
        {
            disposableObject.Dispose();
            disposableObject = null;
        }
    }
}

You can call the method like this:

Disposable.DisposeIfNotNull(ref someDisposableObject);

This is not going to work for properties since you cannot pass a property to a ref parameter. To make it work for Properties as well, you can use expressions:

public static class Disposable
{
    public static void Dispose(Expression<Func<IDisposable>> expression)
    {
        var obj = expression.Compile().Invoke();
        if (obj == null)
            return;

        obj.Dispose();

        var memberExpression = expression.Body as MemberExpression;
        if (memberExpression == null || !IsMemberWritable(memberExpression.Member))
            return;

        var nullExpression = Expression.Constant(null, memberExpression.Type);
        var assignExpression = Expression.Assign(memberExpression, nullExpression);
        var lambdaExpression = Expression.Lambda<Action>(assignExpression);

        var action = lambdaExpression.Compile();
        action.Invoke();
    }

    private static bool IsMemberWritable(MemberInfo memberInfo)
    {
        var fieldInfo = memberInfo as FieldInfo;
        if (fieldInfo != null)
            return !fieldInfo.IsInitOnly && !fieldInfo.IsLiteral;

        var propertyInfo = memberInfo as PropertyInfo;
        if (propertyInfo != null)
            return propertyInfo.CanWrite;

        return true;
    }
}

This method works with variables, fields and properties. It disposes any disposable object, but only sets it to null if it is writable.

You can dispose anything in the same way, as illustrated by the method Foo.CleanUp in the following example:

public class Bar : IDisposable
{
    // ...
}

public class Foo
{
    private Bar _barField = new Bar();

    public Bar BarProperty { get; set; } = new Bar();

    public void CleanUp()
    {
        Disposable.Dispose(() => _barField);
        Disposable.Dispose(() => BarProperty);

        var barVariable = new Bar();
        Disposable.Dispose(() => barVariable);
    }
}
lauxjpn
  • 4,749
  • 1
  • 20
  • 40