Saturday, June 13, 2026

C# Difference Between Reference Type and Runtime Type

In this post, you will see the difference between Reference Type and Runtime Type in C#. One of the most important concepts in C# object-oriented programming is understanding the difference between reference type and runtime type. Many concepts such as upcasting, downcasting, polymorphism, virtual dispatch, is, as, and method overriding become much easier once this distinction is clear.

We will use the following classes(Animal and Bird) to understand the differences:

class Animal
{
    public void Eat()
    {
        Console.WriteLine("Animal eats.");
    }
}
class Bird : Animal
{
    public void Fly()
    {
        Console.WriteLine("Bird can fly.");
    }
}

Reference Type (Compile-Time Type)

  • Reference type is the declared type of a reference variable, parameter, property, field, or expression.
  • It is determined by the compiler and remains fixed for that variable.

Example:
Animal animal = new Bird();

Here: animal is a reference type variable that is of type Animal. The compiler treats animal as an Animal reference.

Reference type determines:

  • Which members are accessible
  • Compile-time type checking
  • Which overload is selected
  • Whether explicit casting is required
Example:
Animal animal = new Bird(); 
animal.Eat(); // Allowed
animal.Fly(); // Compile-time error

Even though the actual object is Bird, the compiler only allows members available in Animal.

Runtime Type (Actual Object Type)

  • Runtime type is the actual type of the object created in memory.
  • It is determined at execution time.

Example:
Animal animal = new Bird();

Here: Runtime Type is Bird. The object itself is a Bird.

Runtime type determines:

  • Which overridden virtual method executes
  • Results of is
  • Whether explicit cast succeeds
  • Actual object behavior
Example:
class Animal
{
    public virtual void Speak()
    {
        Console.WriteLine("Animal");
    }
}
class Bird : Animal
{
    public override void Speak()
    {
        Console.WriteLine("Bird");
    }
}
class Program
{
    static void Main(string[] args)
    {
        Animal animal = new Bird();
        animal.Speak();
    }
}

Output:

Bird

Although reference type is Animal, runtime type is Bird, so Bird.Speak() executes.

Upcasting and Downcasting

Upcasting is all about converting reference from derived type to base type.

Bird bird = new Bird();
Animal animal = bird;

Result: Reference Type of variable bird changes from Bird to Animal. But Runtime Type remains the same type Bird.

Downcasting is all about converting reference from base type to derived type.

Animal animal = new Bird();
Bird bird = (Bird)animal;

Result: Reference Type changes from Animal to Bird. But Runtime Type remains the same type Bird. Object type never changes.

Using is operator

Animal animal = new Bird();
Console.WriteLine(animal is Bird);

Output:

True

Reason: The is operator checks runtime compatibility. It evaluates the runtime type of left side expression compatibility with the type given in the right operand.

In the given example, equivalent question is: does the object referenced by animal reference variable actually represent a Bird. The answer is Yes.

Common Misconception

Incorrect statement:

Loosely developer may say: "Bird object was upcasted into Animal type." But this is technically wrong.

The correct statement:

Reference to a Bird object was upcasted to Animal. or Bird object is referenced through an Animal variable. The object itself never becomes Animal. 

Remember: In process of upcast and downcast the object itself is never changed to another type (even not to subtype/supertype). The change is only of the type of reference variable that is pointing to the object. The reference type is upcasted/downcasted not the object that is referenced.

"In upcasting and downcasting, the runtime object itself never changes type. Only the compile-time type through which the object is accessed (reference/expression type) changes."

Important: Even without reference variable, a conversion can still occur. For example, the expression: ((Bird)new Bird()).Fly();

Summary

  • Reference Type is determined by compiler. But Runtime Type is determined by CLR at runtime.
  • Reference Type is of Variable / expression type. But Runtime Type is of actual object type.
  • Reference Type decides what members can be accessed. But Runtime Type decides which overridden implementation executes.

No comments:

Post a Comment

Hot Topics