Casting as two interfaces simultaneously?

Mike_R

Junior Contributor
Joined
Oct 20, 2003
Messages
316
Location
NYC
I stumbled upon the following code in Juval Lowy's "Programming .NET components" on pages 95-96. The following is pretty much as he shows it, with the addition of a couple of MessageBox calls:
Code:
internal interface IMyInterface
{
    void SomeMethod();
}

internal class MyClass : IMyInterface, IDisposable
{
    void IDisposable.Dispose()
    {
        MessageBox.Show("IDisposable.Dispose() called.");
    }

    void IMyInterface.SomeMethod()
    {
        MessageBox.Show("IMyInterface.SomeMethod() called.");
    }
}

public static void Run()
{
    IMyInterface obj = new MyClass();
    using (obj as IDisposable)
    {
        obj.SomeMethod();
    }
}

The above code runs fine... but this is exactly what I'm questioning...

The point that Lowy was making is that while this code is run fine:

Code:
MyClass obj = new MyClass();
using (obj)
{
    obj.SomeMethod();
}

The following code does not compile:

Code:
IMyInterface obj = new MyClass();
using (obj)
{
    obj.SomeMethod();
}

The reason is that the IMyInterface interface does not itself implement IDisposable. We could correct this situation by having IMyInterface implement IDisposable:

Code:
internal interface IMyInterface: IDisposable
{
    void SomeMethod();
}
But this would be a ludicrous requirement just so that a 'using' statement could be utilized more easily; certainly not all classes implementing IMyInterface would also need to implement IDisposable.

So the solution that Juval Lowy shows is the following:

Code:
public static void Run()
{
    IMyInterface obj = new MyClass();
    using (obj as IDisposable)
    {
        obj.SomeMethod();
    }
}

To my surprise, it does compile and run 100% fine. But what is going on here? Calling 'obj.SomeMethod()' is really a call to 'IMyInterface.SomeMethod()' and so the 'obj' is still cast as 'IMyInterface'. And yet the using statement accepts the cast to IDisposable at the same time!

Admittedly, a 'using' statement does not actually "require" an IDisposable object; the object merely needs to have a method called "Dispose()", and reflection is done at compile time to enforce that the "Dispose()" method does indeed exist. But somehow, I don't think that this is what's going on here. It does seem to be a "simultaneous cast" of sorts...

Does anyone have any thoughts?
 
The compile should enforce the fact that the object implements IDisposable, this isn't a runtime check.

The line code
C#:
obj as IDisposable
however will attempt to cast obj to IDisposable at runtime and return either a valid instance of the interface or null if obj doesn't implement IDisposable.

As far as the compiler is concerned though it acts as if the cast will always work and behaves correctly regardless - the compiler generated equivalent to the using statement is pretty much
C#:
try
{
//use obj here
}
finally
{
if (obj != null)
    obj.Dispose();
}

so even when the cast results in null the issue is dealt with anyway.
 
The compile should enforce the fact that the object implements IDisposable, this isn't a runtime check.

The line code
C#:
obj as IDisposable
however will attempt to cast obj to IDisposable at runtime and return either a valid instance of the interface or null if obj doesn't implement IDisposable.

As far as the compiler is concerned though it acts as if the cast will always work and behaves correctly regardless - the compiler generated equivalent to the using statement is pretty much
C#:
try
{
//use obj here
}
finally
{
if (obj != null)
    obj.Dispose();
}

so even when the cast results in null the issue is dealt with anyway.

Yes, I understand how it works... (I should have mentioned the 'if (obj != null)' check in the finally statement.) But you are right in that I was making a mistake of considering the 'as' operator as a compile time check, instead of as the runtime check that it is.

But this alone does not get us out of hot water...

The mystery that remains is that 'obj' is cast both as IMyInterface -- allowing for the 'obj.SomeMethod()' call -- while simultaneously allowing for the 'cast as IDisposable' call to also be in play... that is 'obj.SomeMethod()' will always work, and yet the check for 'obj != null' within the finally statement may or may not return true.

For example, if the former works (which it does) then the following should fail at runtime:

First let's create a new class 'MyOtherClass', which implements IMyInterface but does NOT implement IDisposable:
Code:
class MyOtherClass : IMyInterface
{
    void IMyInterface.SomeMethod()
    {
        MessageBox.Show("IMyInterface.SomeMethod() called.");
    }
}
then let try calling the following code:
Code:
IMyInterface obj2 = new MyOtherClass();
using (obj2 as IDisposable)
{
    obj2.SomeMethod();
}
One would think that 'obj2 as IDisposable' would return null (it has to) and yet, somehow, call to 'obj2.SomeMethod()' *succeeds* while presumably the null check within the implied finally block of the using statement indicates that the object is also null (because 'obj2 as IDisposable' must return null).

I suppose the answer is that something special is going on here... 'obj' is not null for it does not receive the result of 'obj2 as IDisposable'. That is, the call isn't an assignment such as 'obj2 = obj2 as IDisposable', in which case obj2 would indeed be null.

So here 'obj2' remains non-null and the call to 'obj2.SomeMethod()' succeeds... Therefore, the result of 'obj2 as IDisposable' must be assigned to some temporary variable, and it is this variable that must be checked within the finally statement.

One would have to look at the generated IL code to be certain, but I think that this must be it. Mystery solved.
 
Last edited:
Ultimately the actual object in memory is of type MyClass and IDisposable and IMyInterface (and Object if we are being strict on things) regardless of the type we have assigned to the variable obj - the compiler simply enforces that the type we choose to treat it as is supported by the object itself. Hence we could have decalred obj as Object, IDisposable, IMyInterface or MyClass without problems but any other type would have failed.

The .Net runtime always knows the actual object / interfaces supported at runtime regardless of the variable declaration (if you put a break point on the line IMyInterface obj = new MyClass(); notice that the various debug windows show obj as being of type MyClass rather than of IMyInterface.

Declaring a variable (or parameter) of type IMyInterface simply indicates how we currently wish to interact with this object, the compiler and IDE help through use of intellisense and validating data types and method names etc. but this doesn't change the type of object we are dealing with - just how we are currently perceiving it.

When we use the syntax
C#:
using (obj as IDisposable)
the end result is the same as if we had done something like
C#:
object o = obj;
IDispose id = o as IDisposable
using (id)

If obj (or o as it points to the same place) point to an in memory object that supports IDisposable then id will point to a valid object with a valid IDisposable implementation.

Using your example with obj2 which doesn't implement IDisposable then the code
C#:
object o = obj2;
IDispose id = o as IDisposable
using (id)
would result in id being null; effectively the equivalent of
C#:
using (null)
{
//....
}
which, although pointless, is still perfectly legitimate code that compiles and runs without problems.

Notice none of the obj as IDisposable or similar lines are actually changing what obj points to so it is never being assigned a null value - if you tried that with code similar to
C#:
IMyInterface obj = new MyClass();
obj = obj as IDisposable;
using (obj)
the compiler will prevent compilation due to the clash of data types.
 
Nice explanation. :-)

But I'm not sure that I agree with all of it...

Ultimately the actual object in memory is of type MyClass and IDisposable and IMyInterface (and Object if we are being strict on things) regardless of the type we have assigned to the variable obj - the compiler simply enforces that the type we choose to treat it as is supported by the object itself...
I'm not sure that this is right...

PlausiblyDamp said:
Hence we could have decalred obj as Object, IDisposable, IMyInterface or MyClass without problems but any other type would have failed.

The .Net runtime always knows the actual object / interfaces supported at runtime regardless of the variable declaration (if you put a break point on the line IMyInterface obj = new MyClass(); notice that the various debug windows show obj as being of type MyClass rather than of IMyInterface.
What you say about the debugger may be true, but the debugger is running at runtime. Note that the following code does not compile:
Code:
IMyInterface obj = new MyClass(); 
using (obj) 
{
    obj.SomeMethod(); 
}
(In fact, this is actually the start of this entire exercise.) Because the IMyInterface interface does not implement IDisposable, the 'obj' stored as an IMyInterface reference cannot be used directly within a using statement.

PlausiblyDamp said:
C#:
using (null)
{
//....
}
which, although pointless, is still perfectly legitimate code that compiles and runs without problems.
But the above does still have one minor remaining issue. It must test if the value passed into the using statement is null from within the finally block. Therefore, this value must be stored within a temporary variable.

Not to be picky, but my thinking currently is that the following representation of a using statement -- which is how Juval Lowy shows it on p.94 of his book -- is not *quite* correct:
Code:
try
{
    obj.SomeMethod()
}
finally
{
    if (obj != null)
    obj.Dispose();
}
I think that it must be something like this:
Code:
private IDisposable __hiddenObj = obj as IDisposable;
try
{
    obj.SomeMethod()
}
finally
{
    if (__hiddenObj != null)
    __hiddenObj.Dispose();
}
This is only a subtle change, but I think this is essentially the only way to achieve the simultaneous casting of two interfaces for one object: you need a second variable.
 
Last edited:
What you say about the debugger may be true, but the debugger is running at runtime. Note that the following code does not compile:

Code:
IMyInterface obj = new MyClass(); 
using (obj) 
{
    obj.SomeMethod(); 
}

(In fact, this is actually the start of this entire exercise.) Because the IMyInterface interface does not implement IDisposable, the 'obj' stored as an IMyInterface reference cannot be used directly within a using statement.

The debugger may be running at runtime but so is the runtime ;) if you were to write code like
C#:
MyClass m = new MyClass();
IMyInterface obj = m; 
IDisposable d = m;
then the memory for a new instance of MyClass would be allocated from the heap and the three variable m, obj and d would all point to exactly the same place (effectively they would contain the same memory location as their 'value'); the runtime however is fully aware of the associated type information belonging to the object in memory - including base classes and interfaces supported (this is how runtime casts and conversions are validated at runtime after all).

So although the IMyInterface via the variable obj doesn't implement the IDisposable interface the underlying object (m) itself does and this is what is being checked at runtime when any runtime cast is performed (IDisposable or otherwise).

When looking at casting and type safety the compiler and the runtime have different sets of information to work with; the compiler can only go off the statically hard-coded information such as the declared types and hard-coded casts - if we declare a variable as being IMyInterface that is all the compiler can ever know about it. The runtime however has access to the actual instance of the object and can interrogate the full set of metadata which includes the full list of types derived from and interfaces implemented.

At compile time
C#:
obj =(IMyInterface) new Form();
is perfectly valid - the compiler is taking our word that a Form implements IMyInterface at runtime however it results in an InvalidCast exception based on the runtime type information of a Form.

As to the idea of a temporary variable being introduced by the compiler - they do that sort of thing all the time, and in this case it is nothing more than another 'pointer' to the real underlying object anyway.
 
Hey PD,

I think we're on the same page... If anything, you're probably just mystified as to why I even found this interesting?!

Basically, I was initially confused by the fact that the following will not compile:
Code:
IMyInterface obj = new MyClass();
using (obj)
{
    obj.SomeMethod();
}
But this is not a huge mystery in that IMyInterface does not implement IDisposable. However the 'obj' reference in question in this case does implement IDisposable because it holds a reference to a MyClass object. But the compiler can't know that...

I found the following solution interesting in that the 'obj' in question could function as an IMyInterface -- as exhibited by the successful call to obj.SomeMethod() -- and yet simultaneously operate 'as IDisposable':
Code:
IMyInterface obj = new MyOtherClass();
using (obj as IDisposable)
{
    obj.SomeMethod();
}
It took me a bit, but it turns out I was simply a little too hung up on the slightly-oversimplified explanation of the using statement that Juval Lowy provided in his book:
Code:
try
{
    obj.SomeMethod()
}
finally
{
    if (obj != null)
    obj.Dispose();
}
In the above, the compiler should not be able to verify that 'obj.SomeMethod()' and 'obj.Dispose()' both work and therefore should fail to compile. This is because SomeMethod() is a member of IMyInterface while Dispose() is a member of IDisposable, and the 'obj' cannot be cast to both IMyInterface and to IDisposable at the same time.

Admittedly, this example shown by Lowy was mearly meant to be demonstrative, as a simplification... And I took it a bit too literally. That said, for the picky among us (and I guess that includes me!) the correct translation must be more like the following:
Code:
IDisposable _temp = obj as IDisposable;
try
{
    obj.SomeMethod()
}
finally
{
    if (_temp != null)
    _temp.Dispose();
}
So, anyway, no great mystery, but there was a contradiction, at least the way I was looking at it.

Thanks for helping me get to the bottom of it, even if I was the only one who was confused!
 
Back
Top