[Mono-list] Base class casting weirdness

Mark Hurley debian4tux@telocity.com
Thu, 17 Jan 2002 00:51:42 -0500

On Thu, Jan 17, 2002 at 01:13:11AM +0000, Daniel Lewis wrote:
> Hi,
> I wonder if anyone can shed light on this. In C#, the 'base' keyword is
> used to access methods and constructors of base classes:
> class A {
> 	public virtual string Foo () { return "A"; }
> }
> class B : A {
> 	public override string Foo () { return base.Foo (); }
> }
> Invoking Foo on an instance of B returns "A", as you'd expect. The C#
> programmer's reference that is packaged with the SDK states that:
> 	base.Foo ()
> .. called in an instance method of B is exactly equivalent to writing:
> 	((A)this).Foo ()

I have reason to believe this SDK doc is wrong.  Invoking Foo thru the
"base" [base.Foo()] disables the virtual invocation on A, and treats
the base method [Foo()] as a non-virtual method.

[((A)this).Foo ()]  has the same runtime type of  [B.Foo ()].  This
causes your StackOverflowexception, because B continues to recursively
invoke itself, not the Foo in A because the one in A is virtual.

This is considered normal behavior, and I read thru two books and the
MSDN before I found this.  :)  whew!

If you "hide" B.Foo with something like:

        new private string Foo () {}

...then C can still override A, and you can access A via the
base-access [base.].

> And that in fact this is what the compiler replaces it with at compile
> time.  But if you try to implement it this way:

Here is sample IL code the compiler generates:


  public override string Foo () { return base.Foo (); }

We have this call:

  IL_0001:  call       instance string ConsoleApplication2.A::Foo()

And for this:

  public override string Foo () { return ((A)this).Foo(); }

we end up with:

  IL_0001:  callvirt   instance string ConsoleApplication2.A::Foo()

note the difference between [call] and [callvirt].  The latter is used
to call virtual methods and interface methods, while [call] is used to
call static and nonvirtual methods.

> 	public override string Foo () { return ((A)this).Foo (); }
> CSC will compile it, but you'll get a StackOverflowException at runtime.
> Why would I want to write it this way instead of using 'base'? Well, I've
> got a class C derived from B, that wants to implement Foo by invoking A's
> definition. Can anyone think of a way to do this? Is the runtime
> exception correct, or is this an SDK bug? And what does MCS/Mono do with
> this?

Mark Hurley