tags:

views:

204

answers:

6

Fixed issues with the code. Ok, I think I need to clarify the question.

new A.example(); outputs "A"

What should be inside the example method so that it could output "???"? Is that even possible?

public class Letter {

    public virtual void AsAString() {
         Console.WriteLine("???");
    }
    public void example() {
         this.AsAString();
    }
}

public class A : Letter {
  public override void AsAString() { Console.WriteLine("A"); }
  public void example2() { base.AsAString(); }
}

new A().example2();
new A().example();
+1  A: 

That's very easy:

public void example() {
     Console.WriteLine("???");
}

From that answer you should realise that what you actually asked for wasn't what you thought that you asked for...

If you mean that you want to call the virtual method as if it wasn't virtual, that is not possible. Even if you cast the reference to the base class, it still uses the actual type of the object to determine which method to call.

Guffa
So there is no way at all to invoke a virtual method even from a different base method once it has been overridden?I was well aware when I wrote it that some smart ass could try and answer the question like that. I just didn't really think that the stack overflow community was more intent on one upping each other than on being helpful. I have never had an experience on stack overflow that was this terrible. Though I guess I have never asked a question to the c# community before.
thirsty93
No, it's not possible. When you call the virtual method it's the actual class of the object that determines which method to call, and there is no way to change that.
Guffa
I think I understand the situation now. If I use the new keyword instead inheritance seems to work in away that I guess I'm more used to. I don't really understand what advantages virtual/override offer over using new in the child though except that with override the base class can invoke methods of the child.
thirsty93
Using the new keyword means that you are shadowing the method instead of overriding it. When you call the method the type of the reference decides which method is call instead of the type of the object. Instead of a virtual method you just have separate methods with the same signature.
Guffa
So If I had an array of Letter and I put a object of A in the 0 index and went LetterArray[0]->AsAString() I would get "???" if I had defined AsAString() in A using a new, but if I had used virtual/override I would get "A". Is that correct?
thirsty93
Yes, that is correct.
Guffa
It is actually possible to call a base implementation of an overridden virtual method in .NET, just not from C#. However, MSIL itself allows it (it's how `base.Foo()` works), and C++/CLI can access it; or, if you generate appropriate MSIL (e.g. using `DynamicMethod`), you could do it from C# as well.
Pavel Minaev
+1  A: 

Hi Thirsty93,

Replying to your question, I don't think it is possible. Once you override the method, unless you use the base accessor to access the base class implementation, your call will be to the "most specialized" method you have (in this case the method in AsASString defined in A, instead of the AsASString defined in letter.

The suggestion that Woot4Moo put together will not work since AsASString() and this.AsASString() are accessing the same method, IMHO (the implementation of that method in A).

As you found out, base.AsASString() actually calls the base method, instead of the specialized method.

I hope this helps.

Cheers, Wagner.

Wagner Silveira
Yes, it does, thank you. This whole new vs virtual/override thing is strange to wrap my head around for some reason.
thirsty93
+2  A: 

Let's first make sure that I am interpreting your question correctly. You have classes defined as above. You are instantiating an instance of A and invoking a method example that A inherits from the base class. You want to know if it's possible for the call this.AsAString() in the method Letter.Example to invoke the base implementation of AsAString rather than the derived implementation.

First, let's understand why with example defined as above, invoking Letter.example via an instance of A (e.g., new A().example) will cause A.AsAString to be invoked. From the specification (§7.4.4):

The function member implementation to invoke is determined:

If the compile-time type of E is an interface, the function member to invoke is the implementation of M provided by the run-time type of the instance referenced by E. This function member is determined by applying the interface mapping rules (§13.4.4) to determine the implementation of M provided by the run-time type of the instance referenced by E.

Otherwise, if M is a virtual function member, the function member to invoke is the implementation of M provided by the run-time type of the instance referenced by E. This function member is determined by applying the rules for determining the most derived implementation (§10.6.3) of M with respect to the run-time type of the instance referenced by E.

Otherwise, M is a non-virtual function member, and the function member to invoke is M itself.

So now let's consider your situation. You have an instance a of a class A that derives from Letter. You have invoked a method named example via the syntax a.example(). This will invoke Letter.example which has definition:

public void example() {
    this.AsAString();
}

This will invoke Letter.AsAString. But, Letter.AsAString is declared virtual and therefore, by the bolded rule above, the method that is invoked is A.AsAString because this is of type A, A derives from Letter, and A provides an override of Letter.AsAString.

Now, if you change the definition of A.AsAString so that it hides the base implementation using the new modifier

public new void AsAString() {
    Console.WriteLine("A");
}

then a.example will cause the base implementation to be used and you will see the output ??? as you desire. This is because, by the rule above, the most derived implementation of Letter.AsAString (i.e., the most derived type in the hierarchy of A that provides a definition of the virtual method AsAString) is the base implementation. The new modifier allows A to have a method named AsAString with the same signature as Letter.AsAString but it is not a virtual method.

Please let me know if I am interpreting your question incorrectly, or if any of the above requires clarification.

Jason
+1  A: 

The virtual keyword in C# behaves the same it does in most other languages - specifically, the concrete method that gets invoked is determined by the actual runtime type of the instance.

It seems that what you're looking for is just plain method hiding. The program:

class A
{
    public void Foo()
    {
        Console.WriteLine("Foo called from A");
    }

    public virtual void Bar()
    {
        Console.WriteLine("Bar called from A");
    }

    public virtual void Baz()
    {
        Console.WriteLine("Baz called from A");
    }
}

class B : A
{
    public new void Foo()
    {
        Console.WriteLine("Foo called from B");
    }

    public override void Bar()
    {
        Console.WriteLine("Bar called from B");
    }

    public override void Baz()
    {
        base.Baz();
        Console.WriteLine("Baz called from B");
    }
}

static void Main()
{
    A a = new A();
    a.Foo();
    a.Bar();
    a.Baz();

    B b = new B();
    b.Foo();
    b.Bar();
    b.Baz();

    A a2 = new B();
    a2.Foo();
    a2.Bar();
    a2.Baz();
}

Will produce the following output:

Foo called from A
Bar called from A
Baz called from A

Foo called from B
Bar called from B
Baz called from A
Baz called from B

Foo called from A
Bar called from B
Baz called from A
Baz called from B

Let's break this down:

  • Method A.Foo() is a non-virtual method that is hidden by B. If you call Foo on a variable declared as an A, you will always invoke A.Foo(), even if it is actually an instance of B.

  • Method A.Bar() is a virtual method that is overridden by B. If you call Bar on a variable declared as an A, you will actually invoke B.Foo() if the variable is really an instance of B. That is how virtual dispatch works, according to spec.

  • Method A.Baz() is also virtual, but B.Baz() also invokes the base version before running its own code. That is why you see two lines of output from that method in the last two sets. Only a derived class can invoke the virtual base method - there is no way to invoke it from the outside.

So if you need to be able to execute both the base and derived methods, don't make the method virtual. Hide or shadow it in the derived class.

Aaronaught
A: 

The solution to this is declare A.AsAString like so:

public new void AsAString() { Console.WriteLine("A"); }

This may not suit your needs as ((Letter)new A()).AsAString() will return "???". If that suits your needs, then you might as well not declare it as virtual in the first place.

So, you either allow your overriden function to be called throug hteh base reference or you don't. Kinda you can't have the cacke and eat it too.

Another way to think about this is that such solution, if it existed, would break encapsulation rather badly. The class should be the authority on how it interacts with the base class. To the users of the class, the exact imlementation of which base function gets called should be opaque. A.AsAString() must be the authority on its interaction with Letter.AsAString(), not anyone outside it.

To implement what you want you would need to do add another function like so: public class Letter {

    public virtual void AsAString() {
        BaseAsAString();
    }
    public virtual void BaseAsAString() {
        Console.WriteLine("???");
    }
    public void example() {
        this.AsAString();
    }
}

public class A : Letter {
    public override void AsAString() { Console.WriteLine("A"); }
}

A a = new A();
a.AsAString(); //A
a.BaseAsAString(); //???
Igor Zevaka
+1  A: 

You seem a bit confused about how virtual methods work. Here's a good way to think about it. Imagine that every instance of every class has a certain number of "slots". At runtime, a slot contains a method. To call a method, you tell the runtime "call whatever method is located in slot x of this object".

When you make an "abstract" method, it makes a new slot and puts nothing in it.

When you make a "virtual" method, that makes a new slot and puts the method in the slot.

When you make an "override" method, that does not make a new slot. It "overrides" by replacing whatever is in the previously-declared virtual method slot.

When you make a "new" method, it DOES make a new slot. That's what the "new" means.

When you make a normal method with no annotation, it acts like a "new" method.

When you write code that calls a method, the compiler works out what slot you're talking about and generates code that tells the runtime "call whatever method happens to be in this slot of this object."

The exception to this rule is the "base" call; that generates code that means "ignore what's in the slot and call the base class version of this method".

Is that now clear?

Eric Lippert
Now that's clear... thanks :)
mumtaz