Using a qualified-id to call a base class' function works irrespectively of what happens to that function in the derived class - it can be hidden, it can be overridden, it can be made private (by using a using-declaration), you're directly accessing the base class' function when using a qualified-id.
Explanation: A base class pointer can point to a derived class object, but we can only access base class member or virtual functions using the base class pointer because object slicing happens when a derived class object is assigned to a base class object.
An override method is a new implementation of a member that is inherited from a base class. The overridden base method must be virtual, abstract, or override. Here the base class is inherited in the derived class and the method gfg() which has the same signature in both the classes, is overridden.
base (C# Reference)The base keyword is used to access members of the base class from within a derived class: Call a method on the base class that has been overridden by another method. Specify which base-class constructor should be called when creating instances of the derived class.
There's a difference between new
and virtual
/override
.
You can imagine, that a class, when instantiated, is nothing more than a table of pointers, pointing to the actual implementation of its methods. The following image should visualize this pretty well:
Now there are different ways, a method can be defined. Each behaves different when it is used with inheritance. The standard way always works like the image above illustrates. If you want to change this behavior, you can attach different keywords to your method.
The first one is abstract
. abstract
methods simply point to nowhere:
If your class contains abstract members, it also needs to be marked as abstract
, otherwise the compiler will not compile your application. You cannot create instances of abstract
classes, but you can inherit from them and create instances of your inherited classes and access them using the base class definition. In your example this would look like:
public abstract class Person
{
public abstract void ShowInfo();
}
public class Teacher : Person
{
public override void ShowInfo()
{
Console.WriteLine("I am a teacher!");
}
}
public class Student : Person
{
public override void ShowInfo()
{
Console.WriteLine("I am a student!");
}
}
If called, the behavior of ShowInfo
varies, based on the implementation:
Person person = new Teacher();
person.ShowInfo(); // Shows 'I am a teacher!'
person = new Student();
person.ShowInfo(); // Shows 'I am a student!'
Both, Student
s and Teacher
s are Person
s, but they behave different when they are asked to prompt information about themselves. However, the way to ask them to prompt their information, is the same: Using the Person
class interface.
So what happens behind the scenes, when you inherit from Person
? When implementing ShowInfo
, the pointer is not pointing to nowhere any longer, it now points to the actual implementation! When creating a Student
instance, it points to Student
s ShowInfo
:
The second way is to use virtual
methods. The behavior is the same, except you are providing an optional default implementation in your base class. Classes with virtual
members can be instanciated, however inherited classes can provide different implementations. Here's what your code should actually look like to work:
public class Person
{
public virtual void ShowInfo()
{
Console.WriteLine("I am a person!");
}
}
public class Teacher : Person
{
public override void ShowInfo()
{
Console.WriteLine("I am a teacher!");
}
}
The key difference is, that the base member Person.ShowInfo
isn't pointing to nowhere any longer. This is also the reason, why you can create instances of Person
(and thus it does not need to be marked as abstract
any longer):
You should notice, that this doesn't look different from the first image for now. This is because the virtual
method is pointing to an implementation "the standard way". Using virtual
, you can tell Persons
, that they can (not must) provide a different implementation for ShowInfo
. If you provide a different implementation (using override
), like I did for the Teacher
above, the image would look the same as for abstract
. Imagine, we did not provide a custom implementation for Student
s:
public class Student : Person
{
}
The code would be called like this:
Person person = new Teacher();
person.ShowInfo(); // Shows 'I am a teacher!'
person = new Student();
person.ShowInfo(); // Shows 'I am a person!'
And the image for Student
would look like this:
new
is more a hack around this. You can provide methods in generalized classes, that have the same names as methods in the base class/interface. Both point to their own, custom implementation:
The implementation looks like the one, you provided. The behavior differs, based on the way you access the method:
Teacher teacher = new Teacher();
Person person = (Person)teacher;
teacher.ShowInfo(); // Prints 'I am a teacher!'
person.ShowInfo(); // Prints 'I am a person!'
This behavior can be wanted, but in your case it is misleading.
I hope this makes things clearer to understand for you!
Subtype polymorphism in C# uses explicit virtuality, similar to C++ but unlike Java. This means that you explicitly have to mark methods as overridable (i.e. virtual
). In C# you also have to explicitly mark overriding methods as overriding (i.e. override
) to prevent typos.
public class Person
{
public virtual void ShowInfo()
{
Console.WriteLine("I am Person");
}
}
public class Teacher : Person
{
public override void ShowInfo()
{
Console.WriteLine("I am Teacher");
}
}
In the code in your question, you use new
, which does shadowing instead of overriding. Shadowing merely affects the compile-time semantics rather than the runtime semantics, hence the unintended output.
You have to make the method virtual and you have to override the function in the child class, in order to call the method of class object you put in parent class reference.
public class Person
{
public virtual void ShowInfo()
{
Console.WriteLine("I am Person");
}
}
public class Teacher : Person
{
public override void ShowInfo()
{
Console.WriteLine("I am Teacher");
}
}
When a virtual method is invoked, the run-time type of the object is checked for an overriding member. The overriding member in the most derived class is called, which might be the original member, if no derived class has overridden the member. By default, methods are non-virtual. You cannot override a non-virtual method. You cannot use the virtual modifier with the static, abstract, private or override modifiers, MSDN.
You are using new key word instead of override, this is what new does
If the method in the derived class is not preceded by new or override keywords, the compiler will issue a warning and the method will behave as if the new keyword were present.
If the method in the derived class is preceded with the new keyword, the method is defined as being independent of the method in the base class, This MSDN article explains it very well.
We have early binding at compile time for normal method (not virtual) which is the currrent case the compiler will bind call to method of base class that is method of reference type (base class) instead of the object is held in the referece of base class i.e. derived class object. This is because ShowInfo
is not a virtual method. The late binding is performed at runtime for (virtual / overridden method) using virtual method table (vtable).
For a normal function, the compiler can work out the numeric location of it in memory. Then it when the function is called it can generate an instruction to call the function at this address.
For an object that has any virtual methods, the compiler will generate a v-table. This is essentially an array that contains the addresses of the virtual methods. Every object that has a virtual method will contain a hidden member generated by the compiler that is the address of the v-table. When a virtual function is called, the compiler will work out what the position is of the appropriate method in the v-table. It will then generate code to look in the objects v-table and call the virtual method at this position, Reference.
I want to build off of Achratt's answer. For completeness, the difference is that the OP is expecting the new
keyword in the derived class's method to override the base class method. What it actually does is hide the base class method.
In C#, as another answer mentioned, traditional method overriding must be explicit; the base class method must be marked as virtual
and the derived class must specifically override
the base class method. If this is done, then it doesn't matter whether the object is treated as being an instance of the base class or derived class; the derived method is found and called. This is done in a similar fashion as in C++; a method marked "virtual" or "override", when compiled, is resolved "late" (at runtime) by determining the referenced object's actual type, and traversing the object hierarchy downward along the tree from the variable type to the actual object type, to find the most derived implementation of the method defined by the variable type.
This differs from Java, which allows "implicit overrides"; for instance methods (non-static), simply defining a method of the same signature (name and number/type of parameters) will cause the subclass to override the superclass.
Because it's often useful to extend or override the functionality of a non-virtual method you do not control, C# also includes the new
contextual keyword. The new
keyword "hides" the parent method instead of overriding it. Any inheritable method can be hidden whether it's virtual or not; this allows you, the developer, to leverage the members you want to inherit from a parent, without having to work around the ones you don't, while still allowing you to present the same "interface" to consumers of your code.
Hiding works similarly to overriding from the perspective of a person using your object at or below the level of inheritance at which the hiding method is defined. From the question's example, a coder creating a Teacher and storing that reference in a variable of the Teacher type will see the behavior of the ShowInfo() implementation from Teacher, which hides the one from Person. However, someone working with your object in a collection of Person records (as you are) will see the behavior of the Person implementation of ShowInfo(); because Teacher's method doesn't override its parent (which would also require Person.ShowInfo() to be virtual), code working at the Person level of abstraction won't find the Teacher implementation and won't use it.
In addition, not only will the new
keyword do this explicitly, C# allows implicit method hiding; simply defining a method with the same signature as a parent class method, without override
or new
, will hide it (though it will produce a compiler warning or a complaint from certain refactoring assistants like ReSharper or CodeRush). This is the compromise C#'s designers came up with between C++'s explicit overrides vs Java's implicit ones, and while it's elegant, it doesn't always produce the behavior you would expect if you come from a background in either of the older languages.
Here's the new stuff: This gets complex when you combine the two keywords in a long inheritance chain. Consider the following:
class Foo { public virtual void DoFoo() { Console.WriteLine("Foo"); } }
class Bar:Foo { public override sealed void DoFoo() { Console.WriteLine("Bar"); } }
class Baz:Bar { public virtual void DoFoo() { Console.WriteLine("Baz"); } }
class Bai:Baz { public override void DoFoo() { Console.WriteLine("Bai"); } }
class Bat:Bai { public new void DoFoo() { Console.WriteLine("Bat"); } }
class Bak:Bat { }
Foo foo = new Foo();
Bar bar = new Bar();
Baz baz = new Baz();
Bai bai = new Bai();
Bat bat = new Bat();
foo.DoFoo();
bar.DoFoo();
baz.DoFoo();
bai.DoFoo();
bat.DoFoo();
Console.WriteLine("---");
Foo foo2 = bar;
Bar bar2 = baz;
Baz baz2 = bai;
Bai bai2 = bat;
Bat bat2 = new Bak();
foo2.DoFoo();
bar2.DoFoo();
baz2.DoFoo();
bai2.DoFoo();
Console.WriteLine("---");
Foo foo3 = bak;
Bar bar3 = bak;
Baz baz3 = bak;
Bai bai3 = bak;
Bat bat3 = bak;
foo3.DoFoo();
bar3.DoFoo();
baz3.DoFoo();
bai3.DoFoo();
bat3.DoFoo();
Output:
Foo
Bar
Baz
Bai
Bat
---
Bar
Bar
Bai
Bai
Bat
---
Bar
Bar
Bai
Bai
Bat
The first set of five is all to be expected; because each level has an implementation, and is referenced as an object of the same type as was instantiated, the runtime resolves each call to the inheritance level referenced by the variable type.
The second set of five is the result of assigning each instance to a variable of the immediate parent type. Now, some differences in behavior shake out; foo2
, which is actually a Bar
cast as a Foo
, will still find the more derived method of the actual object type Bar. bar2
is a Baz
, but unlike with foo2
, because Baz doesn't explicitly override Bar's implementation (it can't; Bar sealed
it), it's not seen by the runtime when looking "top-down", so Bar's implementation is called instead. Notice that Baz doesn't have to use the new
keyword; you'll get a compiler warning if you omit the keyword, but the implied behavior in C# is to hide the parent method. baz2
is a Bai
, which overrides Baz
's new
implementation, so its behavior is similar to foo2
's; the actual object type's implementation in Bai is called. bai2
is a Bat
, which again hides its parent Bai
's method implementation, and it behaves the same as bar2
even though Bai's implementation isn't sealed, so theoretically Bat could have overridden instead of hidden the method. Finally, bat2
is a Bak
, which has no overriding implementation of either kind, and simply uses that of its parent.
The third set of five illustrates the full top-down resolution behavior. Everything is actually referencing an instance of the most derived class in the chain, Bak
, but resolution at every level of variable type is performed by starting at that level of the inheritance chain and drilling down to the most derived explicit override of the method, which are those in Bar
, Bai
, and Bat
. Method hiding thus "breaks" the overriding inheritance chain; you have to be working with the object at or below the level of inheritance that hides the method in order for the hiding method to be used. Otherwise, the hidden method is "uncovered" and used instead.
Please read about polymorphism in C#: Polymorphism (C# Programming Guide)
This is an example from there:
When the new keyword is used, the new class members are called instead of the base class members that have been replaced. Those base class members are called hidden members. Hidden class members can still be called if an instance of the derived class is cast to an instance of the base class. For example:
DerivedClass B = new DerivedClass();
B.DoWork(); // Calls the new method.
BaseClass A = (BaseClass)B;
A.DoWork(); // Calls the old method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With