0

Dispose method does not allow the object to participate in the using in C#. Why?

According to the CLR via C#:

If a dynamic expression is specified as the collection in a foreach statement or as a resource in a using statement, the compiler will generate code that attempts to cast the expression to the non-generic System.IEnumerable interface or to the System.IDisposable interface, respectively. If the cast succeeds, the expression is used and the code runs just fine. If the cast fails, a Microsoft.CSharp.RuntimeBinder.RuntimeBinderException exception is thrown.

So, I am trying the following:

using System;

namespace myprogram
{
    delegate void VoidRes();
    class Program
    {
        static void Main(string[] args)
        {

            dynamic a = new
            {
                Dispose = new VoidRes
                (
                    delegate () { Console.WriteLine("in Dispose"); }
                )
            };

            using(a) {
                Console.WriteLine("foo");
            }
        }
    }
}

And I am getting an error:

Unhandled exception. Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot implicitly convert type '<>f__AnonymousType4' to 'System.IDisposable' at CallSite.Target(Closure , CallSite , Object ) at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)

That is strange, because I thought that in order to satisfy the IDisposable interface it is enough to implement the Dispose method (exactly what I did in my anonymous type).

What am I missing here?

qqqqqqq
  • 1,831
  • 1
  • 18
  • 48
  • 3
    That Dispose is not a method but a delegate field. – H H Nov 25 '19 at 22:35
  • 1
    You could have easily proven that it was working as expected by changing the `dynamic` to say a `var`. Then you would have gotten an error at _compile-time_ indicating that the object does not support `IDisposable`. By using `dynamic`, you won't know until runtime. Besides, just naming something `Dispose` isn't going to trick the CLR –  Nov 25 '19 at 22:50
  • 4
    98 times out 100, dynamic is the wrong approach anyway, you are generally solving one problem by making 2 more. In most cases you should likely rethink your design (unless there is a compelling reason to do so) – TheGeneral Nov 25 '19 at 23:01
  • 1
    @TheGeneral I think you over estimated the valid cases. I have yet to *need* a dynamic object. And have only used them for a few quick POCs. – Austin T French Nov 25 '19 at 23:23
  • 2
    @TheGeneral agreed. About the only time I used `dynamic` was for some cases where I didn't have the type library for COM interop, [apparently one of the things `dynamic` was designed for](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/types/using-type-dynamic#com-interop). Sadly I avoid it like the plague now due to nasty [leak](https://stackoverflow.com/questions/33259334/leak-in-runtimebinder-when-using-dynamic-keyword-with-comobject). A pitty –  Nov 25 '19 at 23:35
  • 1
    @MickyD Yeah, the only reason it turns up on our codebase are when we are forced to do so because of a 3rd party implementation. There is the rare exception where it hacks around something (for-which other solutions are unpalatable). yet i find that this was because of a bad decision someone made for historical reasons anyway. You may now join the fan club PADWTI (People Against Dynamic and Weakly Typed Implementations) – TheGeneral Nov 25 '19 at 23:48
  • @TheGeneral haha. sign me up! –  Nov 26 '19 at 02:05

2 Answers2

3

Your anonymous type doesn't implement the IDisposable interface just because it has a method that the IDisposable interface has. An anonymous type cannot implement an interface. See here: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/anonymous-types

Which states that:

"Anonymous types are class types that derive directly from object, and that cannot be cast to any type except object."

Matt U
  • 4,970
  • 9
  • 28
1

It is enough to implement IDisposable but it didn't happen anywhere in your code. Interface implementation in C# is done by creating a class/struct and explicitly indicating interface type. It's not enough for an object to have the same members (properties, methods, etc.) to match an interface. (That behavior is called duck typing and exists in some other languages.)

Even if it duck typing was possible in C#, this particular case still wouldn't work. It is because new { Dispose = ... } creates an instance of an anonymous class with a property (not a method) called Dispose. Consequently, contract of the IDisposable interface is not satisfied.

Also anonymous classes do not implement interfaces, IDisposable in particular.

pbalaga
  • 2,018
  • 1
  • 21
  • 33