Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

The override modifier in C# is used to provide a new, derived implementation for an inherited method, property, indexer, or event. It is the primary mechanism for achieving run-time polymorphism, instructing the Common Language Runtime (CLR) to utilize virtual method table (v-table) dispatch to resolve member invocations to the most derived implementation in an object’s inheritance hierarchy, regardless of the compile-time reference type. To use the override modifier, the base class member being overridden must be explicitly marked with the virtual, abstract, or override keyword.
public class BaseClass
{
    // The base member must be virtual, abstract, or override
    public virtual void ProcessData(int identifier)
    {
        Console.WriteLine("Base method implementation");
    }

    // Properties can also be marked for overriding
    public virtual string Status { get; set; } = "Base Status";
}

public class DerivedClass : BaseClass
{
    // Overriding a method replaces the base implementation
    public override void ProcessData(int identifier)
    {
        // The 'base' keyword allows invocation of the parent class implementation
        base.ProcessData(identifier); 
        Console.WriteLine("Derived method implementation");
    }

    // Overriding a property modifies its get/set accessors
    public override string Status 
    { 
        get => base.Status + " (Derived)"; 
        set => base.Status = value; 
    }
}

Compiler Rules and Constraints

The C# compiler enforces strict structural parity between the base member and the overriding member:
  • Signature Matching: The overriding member must possess the exact same name, parameter types, parameter order, and ref/out/in modifiers as the inherited base member.
  • Access Modifiers: The overriding member generally cannot change the accessibility of the virtual member. For example, a protected virtual method must be overridden as protected override. The strict exception to this rule occurs when overriding a protected internal member from a different assembly; in this scenario, the overriding member must be declared as protected.
  • Invalid Targets: The override modifier cannot be applied to members marked as static, sealed, or members that are not explicitly marked as virtual, abstract, or override in the base class.
  • Property/Indexer Accessors: When overriding a property or indexer, you cannot add an accessor that does not exist in the base class (e.g., you cannot add a set accessor if the base property is read-only). However, if the base property has both get and set accessors, the derived class is allowed to override only one of them (e.g., just the get accessor) while inheriting the base implementation for the other.
  • Sealing: An overridden member is implicitly virtual to any further derived classes. To terminate the override chain, the sealed keyword can be combined with override (e.g., public sealed override void ProcessData(int identifier)).

Covariant Return Types (C# 9.0+)

Historically, the return type of an overriding method had to match the base method exactly. Starting with C# 9.0, C# supports covariant return types. An overriding method can declare a return type that is strictly derived from the return type declared in the base method.
public class BaseEntity { }
public class DerivedEntity : BaseEntity { }

public class BaseRepository
{
    public virtual BaseEntity GetEntity() => new BaseEntity();
}

public class DerivedRepository : BaseRepository
{
    // Covariance: Returning DerivedEntity instead of BaseEntity
    public override DerivedEntity GetEntity() => new DerivedEntity();
}

override vs. new (Member Hiding)

It is critical to distinguish override from the new modifier. While both allow a derived class to declare a member with the same signature as a base class, their runtime dispatch behaviors differ fundamentally:
  • override: Modifies the v-table. The CLR inspects the actual runtime type of the object instance to determine which member to invoke.
  • new: Shadows the base member without modifying the v-table. The compiler uses the static, compile-time type of the reference variable to determine which member to invoke.
BaseClass obj = new DerivedClass();

// If ProcessData is 'override' in DerivedClass, this calls DerivedClass.ProcessData()
// If ProcessData is 'new' in DerivedClass, this calls BaseClass.ProcessData()
obj.ProcessData(1); 

// If Status is 'override' in DerivedClass, this accesses DerivedClass.Status
// If Status is 'new' in DerivedClass, this accesses BaseClass.Status
Console.WriteLine(obj.Status);
Master C# with Deep Grasping Methodology!Learn More