I am wondering if possible to override a method from calling console app or a super super class? I understand I can override WriteLog at DoMath.... however consider that I would like to manage this instead at the console app.
Example:
public class LogThings
{
public virtual void WriteLog(string value)
{
Console.WriteLine("This is the base in LogThings " value);
}
}
The class inheriting the base. I kind of thought if I add the method again and mark it with new to implement as a virtual, then I can override this in the console app that inherits DoMath?
public class DoMath : LogThings
{
public double DoAddition(double a, double b)
{
double result;
result = a b;
WriteLog(result.ToString()); // < the operation I need to overload
return result;
}
public new virtual void WriteLog(string value)
{
Console.WriteLine("this is overriding the base in DoMath");
base.WriteLog(value);
}
}
Some console app using the doMathANdLog class library:
class Program : DoMath
{
static void Main(string[] args)
{
var m = new DoMath();
m.DoAddition(1, 2);
Console.ReadLine();
}
public override void WriteLog(string value)
{
Console.WriteLine("this is not overriding.");
}
}
Result is this when running it:
this is overriding the base in DoMath
This is the base in LogThings 3
Is there a way to do this?
CodePudding user response:
As noted in the comments, DoMath
provides a new implementation (it's in the definition there) of the WriteLog
method. Your Program
class then overrides that implementation.
Where it's 'failing' is here:
var m = new DoMath();
You're not creating an instance of Program
with its overridden method, so that override is never used. Try new Program()
instead.
CodePudding user response:
That's a simultaneously fun and annoying problem to have! :) I believe that one of the missing links here is this:
- The
override
modifier is related tovirtual
orabstract
methods; a method with anoverride
modifier simply provides an alternative implementation to an existingvirtual
orabstract
method. Avirtual
method has a default implementation, anabstract
method has no implementation at all. - The
new
modifier however can be applied to any method and simply allows the reuse of a name that was already taken. These methods are not related to each other at all, the only similarity being that they forcibly share the same name.
The problem with new
is that the type using a new
method "pretends" that the original implementation never existed. The base class however is absolutely unaware of this - new
severs the link in the hierarchy, and this is what's casing your problem.
Generally speaking, you do not want to use the new
modifier.
That, and you probably wanted to use var m = new Program();
instead - reasons being explained below.
Consider these two pieces of code:
LogThings a = new DoMath();
a.WriteLog("something");
and
LogThings a = new Program();
a.WriteLog("something");
At this point, the method being called is LogThings.WriteLog()
. Even though we instantiate a DoMath
or Program
class that provides a new
method, the LogThings
part of the world doesn't "know" that. It instead believes to have a virtual
method that doesn't happen to be overridden. As a result, this code prints:
This is the base in LogThings something
As mentioned above: new
severs that link.
In the next example, the method being called is indeed DoMath.WriteLog()
, because we're now simply instantiating the DoMath()
class and call its LogThings
method.
DoMath b = new DoMath();
b.WriteLog("something");
Not surprisingly, this code prints
this is overriding the base in DoMath
This is the base in LogThings something
Note that it does not print "this is not overriding" because we did not instantiate an instance of the Program
class. Likewise, the base.LogThings()
call has nothing to do with "overriding", it simply changes the focus to the LogThings
type and calls whatever implementation it knows.
This is similar to the original code you used:
var m = new DoMath();
Lastly, consider this version:
DoMath c = new Program();
c.WriteLog("something");
Here, the Program
class actually overrides
the virtual void WriteLog
method of DoMath
. Consequently, this code prints
this is not overriding.
... which is now wrong, because it does.
The key to understanding this is that each class containing virtual or abstract methods has what's called a virtual function table, or vtable. This vtable is "inherited" by derived classes and allows the compiler to know which implementation of a method to call (through a so-called virtual dispatch).
You can consider an entry in the vtable to be something like a pointer to the actual implementation of a method (the one from the current class), followed by a pointer to the previous implementation.
In your example of DoMath
and Program
, instantiating the DoMath
class would produce a vtable consisting of only
DoMath.WriteLog(string) -> null
whereas instantiating the Program
class would produce an entry like this:
Program.WriteLog(string) -> DoMath.WriteLog(string) -> null
This is why ((DoMath)new Program()).WriteLog()
works - even though we look at a DoMath
reference, the compiler looks up the vtable for the instantiated type (Program
) and can follow the chain up to the actual implementation (Program.WriteLog
).
Do however note the makeshift null
in there. Because the DoMath
class declares the WriteLog
method as new
, it is considered to be a - well - new method, which is unrelated to the one from LogThings
. For LogThings
, the vtable world still looks somewhat like this:
LogThings.WriteLog(string) -> null
Because there is no legit override
- just a different method that happens to have the same name - ((LogThings)new Program()).WriteLog()
calls LogThings.WriteLog()
, as that's the last implementation in the chain. The new
essentially "forces in" another vtable, resulting in this somewhat split-brained setup.
Please note that this description of a vtable is drastically oversimplified; there's plenty of good material out there on that topic however.