The virtual
keyword allows a method, property, indexer or event to be overridden by derived classes and present polymorphic behavior. (Members are non-virtual by default in C#)
public class BaseClass
{
public virtual void Foo()
{
Console.WriteLine("Foo from BaseClass");
}
}
In order to override a member, the override
keyword is used in the derived classes. (Note the signature of the members must be identical)
public class DerivedClass: BaseClass
{
public override void Foo()
{
Console.WriteLine("Foo from DerivedClass");
}
}
The polymorphic behavior of virtual members means that when invoked, the actual member being executed is determined at runtime instead of at compile time. The overriding member in the most derived class the particular object is an instance of will be the one executed.
In short, object can be declared of type BaseClass
at compile time but if at runtime it is an instance of DerivedClass
then the overridden member will be executed:
BaseClass obj1 = new BaseClass();
obj1.Foo(); //Outputs "Foo from BaseClass"
obj1 = new DerivedClass();
obj1.Foo(); //Outputs "Foo from DerivedClass"
Overriding a method is optional:
public class SecondDerivedClass: DerivedClass {}
var obj1 = new SecondDerivedClass();
obj1.Foo(); //Outputs "Foo from DerivedClass"
Since only members defined as virtual
are overridable and polymorphic, a derived class redefining a non virtual member might lead to unexpected results.
public class BaseClass
{
public void Foo()
{
Console.WriteLine("Foo from BaseClass");
}
}
public class DerivedClass: BaseClass
{
public void Foo()
{
Console.WriteLine("Foo from DerivedClass");
}
}
BaseClass obj1 = new BaseClass();
obj1.Foo(); //Outputs "Foo from BaseClass"
obj1 = new DerivedClass();
obj1.Foo(); //Outputs "Foo from BaseClass" too!
When this happens, the member executed is always determined at compile time based on the type of the object.
BaseClass
(even if at runtime is of a derived class) then the method of BaseClass
is executedDerivedClass
then the method of DerivedClass
is executed.This is usually an accident (When a member is added to the base type after an identical one was added to the derived type) and a compiler warning CS0108 is generated in those scenarios.
If it was intentional, then the new
keyword is used to suppress the compiler warning (And inform other developers of your intentions!). the behavior remains the same, the new
keyword just suppresses the compiler warning.
public class BaseClass
{
public void Foo()
{
Console.WriteLine("Foo from BaseClass");
}
}
public class DerivedClass: BaseClass
{
public new void Foo()
{
Console.WriteLine("Foo from DerivedClass");
}
}
BaseClass obj1 = new BaseClass();
obj1.Foo(); //Outputs "Foo from BaseClass"
obj1 = new DerivedClass();
obj1.Foo(); //Outputs "Foo from BaseClass" too!
Unlike in C++, the usage of the override
keyword is not optional:
public class A
{
public virtual void Foo()
{
}
}
public class B : A
{
public void Foo() // Generates CS0108
{
}
}
The above example also causes warning CS0108, because B.Foo()
is not automatically overriding A.Foo()
. Add override
when the intention is to override the base class and cause polymorphic behavior, add new
when you want non-polymorphic behavior and resolve the call using the static type. The latter should be used with caution, as it may cause severe confusion.
The following code even results in an error:
public class A
{
public void Foo()
{
}
}
public class B : A
{
public override void Foo() // Error: Nothing to override
{
}
}
The following code is perfectly valid (although rare):
public class A
{
public void Foo()
{
Console.WriteLine("A");
}
}
public class B : A
{
public new virtual void Foo()
{
Console.WriteLine("B");
}
}
Now all objects with a static reference of B (and its derivatives) use polymorphism to resolve Foo()
, while references of A use A.Foo()
.
A a = new A();
a.Foo(); // Prints "A";
a = new B();
a.Foo(); // Prints "A";
B b = new B();
b.Foo(); // Prints "B";
The C# compiler is strict in preventing senseless constructs. Methods marked as virtual
cannot be private. Because a private method cannot be seen from a derived type, it couldn't be overwritten either. This fails to compile:
public class A
{
private virtual void Foo() // Error: virtual methods cannot be private
{
}
}