Interface Question

  • Thread starter Thread starter walt.stoneburner
  • Start date Start date
W

walt.stoneburner

Why does aliasing an interface fail at runtime?

SAMPLE WORKING CODE
...
IHTMLDocument2 doc = (IHTMLDocument2) browser.Document;
...

BROKEN CODE
public interface IFooBar : IHTMLDocument2 { }
...
IFooBar doc = (IFooBar) browser.Document;
...

The broken code example compiles with no errors or warnings, but causes
an System.InvalidCastException exception at runtime, with a more
detailed message of "Specified cast is not valid."

What I don't get is that if my IFooBar interface inheirts from an
interface that works and contains no other additional method
signatures, then in theory the interfaces should be identical. Yet,
clearly it's failing performing the cast.

What's _really_ going on behind the scenes that explains why C# is
doing this?

Thanks,
-Walt Stoneburner
 
Why does aliasing an interface fail at runtime?

SAMPLE WORKING CODE
...
IHTMLDocument2 doc = (IHTMLDocument2) browser.Document;
...

BROKEN CODE
public interface IFooBar : IHTMLDocument2 { }
...
IFooBar doc = (IFooBar) browser.Document;
...

The broken code example compiles with no errors or warnings, but causes
an System.InvalidCastException exception at runtime, with a more
detailed message of "Specified cast is not valid."

Yes, it would.
What I don't get is that if my IFooBar interface inheirts from an
interface that works and contains no other additional method
signatures, then in theory the interfaces should be identical. Yet,
clearly it's failing performing the cast.

What's _really_ going on behind the scenes that explains why C# is
doing this?

Because you haven't actually got an IFooBar. Just because it's got the
same methods doesn't mean it's the same interface. Here's another
example:

public class Foo
{
}

No extra fields, methods or anything else on top of what's provided by
System.Object, right? But I can't do:

object o = new object();
Foo f = (Foo) o;

- it fails at runtime, just like your example does.

The runtime doesn't care about whether or not an instance meets all the
requirements (in terms of members etc) to implement an interface - it
cares whether the *actual type* of the object implements the interface.
 
Walt,

The reason this doesn't work is because the identity of IFooBar is
different from that of IFooBar. IFooBar extends IHTMLDocument2. However,
when the Document property returns a value, it doesn't have any clue what
IFooBar is, it only knows that it implements IHTMLDocument2. What would the
other methods/properties on IFooBar do if you were allowed to make this
cast? There is no way of defining it in the interface...

Hope this helps.
 
I'm about 80% of the way to emotionally accepting the answer; I see
where Jon Skeet was going, and intellectually accept what he pointed
out.

Here's my real problem: I've got a browser.Document object, and
-three- different interfaces that it supports: IHTMLDocument,
IHTMLDocument2, and IHTMLDocument3... each doing something different.

I'm having to cast and maintain a reference to each flavor of
interface, and this seems silly.

I'd like to be able to just do this:
public IMyDoc : IHTMLDocument, IHTMLDocument2, and IHTMLDocument3 { }

....and have IMyDoc provide all the interfaces combined that I'm going
to use.

If this isn't what the inheirtance-like syntax is for on the interface
definition, what's problem is it solving? Why give us the ability to
list base interfaces?

(And that's where the remaining 20% of my confusion is coming from.)

Thanks all,
-wls
 
I went back and read your's and Jon's posts again, trying to
internalize them into a mental model that makes sense.

Let me see if I'm getting closer...

interface A : B, C { }

This does NOT say that "A is an interface that is the conglomeration of
B and C, feel free to cast to A and use any of the B and C
interfaces..."

Instead, it says "A is a unique interface and type under its own right,
and it just happens to implement the same signatures as B and C -- so,
feel free to cast a returned A interface into B, or even C, but you
can't use A where B and C are used."

....am I any closer in my understanding?
 
Walt,

In addtion to what Jon and Nicholas said regarding the reason why it fails
in run time, I want to try to explaing it doesn't fail at compile time.

It doesn't fail at compile time because you give wrong instructions to the
compiler.

Using explicit cast tells the compiler that you know what you are doing and
you guarantee that the cast is valid even thought it is not obvious.

Re-write the line
IFooBar doc = (IFooBar) browser.Document;

to be

IFooBar doc = browser.Document;
(no explicit cast)

Try to compile this code. The compiler will fire an error because this is
what the compiler knows about the types. IFooBar is not the same as
IHTMLDocument2 and the object returned by the browser.Document doesn't
implement this new interface, it implements its base type. Cast from base to
derived types is not valid in all of the cases, because derived types are
more specialized and might have added new functionality to the base type.

By using explicit cast you basically say - "Don't give me the error. I know
what I'm doing and it is correct". That is OK, but not in this case.

It would be OK if you new that the object returned by browser.Document
implements IFooBar interface, but for some reason you received (via method
parameter for example) reference to IHTMLDocument2.
In this case the line

IFooBar doc = obj;

will give you the compile time error because you try to cast from base to
more specialized type, but because you know that the cast is possible (the
obj actually implements IFooBar) you can tell the compiler that you know
that the cast is valid, so the compiler has to stop bothering you with that
error message. You tell this using explicit cast:

IFooBar doc = (IFooBar)obj;
 
I'm about 80% of the way to emotionally accepting the answer; I see
where Jon Skeet was going, and intellectually accept what he pointed
out.

Here's my real problem: I've got a browser.Document object, and
-three- different interfaces that it supports: IHTMLDocument,
IHTMLDocument2, and IHTMLDocument3... each doing something different.

I'm having to cast and maintain a reference to each flavor of
interface, and this seems silly.

I'd like to be able to just do this:
public IMyDoc : IHTMLDocument, IHTMLDocument2, and IHTMLDocument3 { }

...and have IMyDoc provide all the interfaces combined that I'm going
to use.

I'm afraid that I haven't dealt with browser.Document myself, so it's
hard to know the best way of managing the situation...
If this isn't what the inheirtance-like syntax is for on the interface
definition, what's problem is it solving? Why give us the ability to
list base interfaces?

Two things:

1) You can force anything which implements IDerivedInterface to
implement IBaseInterface in terms of the members it provides, without
specifying them explicitly.

2) It means you can always use something which you know implements
IDerivedInterface in cases which require IBaseInterface. For instance,
if I have a method parameter of type IList, I can pass the value I'm
given to another method which requires something of type IEnumerable,
because anything which implements IList *must* implement IEnumerable
anyway.
 
I went back and read your's and Jon's posts again, trying to
internalize them into a mental model that makes sense.

Let me see if I'm getting closer...

interface A : B, C { }

This does NOT say that "A is an interface that is the conglomeration of
B and C, feel free to cast to A and use any of the B and C
interfaces..."

Instead, it says "A is a unique interface and type under its own right,
and it just happens to implement the same signatures as B and C -- so,
feel free to cast a returned A interface into B, or even C, but you
can't use A where B and C are used."

...am I any closer in my understanding?

Yes, I think so.
 
Stoitcho,

Thank you. You've unwraveled a bigger portion of the mystery for me.
This makes total sense now, and I'm back happily coding.

Many thanks to everyone who provided clarification.
-wls
 
Walt,

In addition to what Jon said, I don't understand why you are trying to
create your own interface. I mean, you can't just derive from an interface,
and think that anything else that implements a ^part^ of the interface you
defined can support the whole definition.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top