Thursday, June 11, 2026

C# Upcast and Downcast

In this post we will understand what is meant by upcast and downcast in programming and will also see the differences between them.
In simple words, upcast means passing value of lower datatype to upper datatype variable. The datatype of value/variable can be value type or reference type.

In case of reference types:
  • Upper datatype is also known as Super type or Parent type or Base type.
  • Lower datatype is also known as Sub type or Child type or Derived type.
  • These types are related types where lower datatype inherits upper datatype.
  • These types shows relationship between types based on inheritance.
  • Upcasting and Downcasting are related to these relationships.

Upcasting w.r.t. Value Types

First, we see examples of value type data which are casted to another value type. The upcast operation is always implicit. You do not need to use cast operator.
class Program
{
    static void Main(string[] args)
    {
        int x = 10;
        double y = x;
        long z = x;
        Console.WriteLine($"The values of x, y and z are {x}, {y} and {z} respectively.");
    }
}
The conversion of int type into double and long is implcit. We did not use cast operator. The above example is all about upcasting. 

Now we look at downcast example. The following code shows errors at compile time. We cannot downcast a upper datatype data into lower type without explicit cast operator:
class Program
{
    static void Main(string[] args)
    {
        double x = 10.25;
        int y = x; // Error at compile time
        long z = x;// Error at compile time
        Console.WriteLine($"The values of x, y and z are {x}, {y} and {z} respectively.");
    }
}
Now we use cast operator to resolve the errors:
class Program
{
    static void Main(string[] args)
    {
        double x = 10.25;
        int y = (int)x;
        long z = (int)x;
        Console.WriteLine($"The values of x, y and z are {x}, {y} and {z} respectively.");
    }
}

Upcasting w.r.t. Reference Types

If you have understood this far with value type upcast and downcast, the reference type upcast and downcast is similar. Look at the following code:
class Program
{
    static void Main(string[] args)
    {

        // Lower reference type employee1 object upcasted to Person type
        Person person = new Employee();
        person.Id = 1;
        Console.WriteLine(person.GetType()); // Employee
        //person.Name = "Error when accessed Name"; // Error
    }

}
class Person
{
    public int Id { get; set; }
}
class Employee : Person
{
    public string Name { get; set; } = "Unknown";
}

Some facts about Upcasting w.r.t. Reference Types

  1. Person is Upper reference type and Employee is Lower reference type.
  2. person is a reference variable which can be assigned null, a Person instance or any instance of subtypes of Person type e.g. Employee.
  3. No Upcast/Downcast: Person person = new Person();
  4. Upcast: Person person = new Employee(); because an employee object is assigned to its upper type i.e. Person reference variable named person. 
  5. The Console.WriteLine(person.GetType()); returns Employee because person is pointing to an employee object (subtype object of Person). The person reference variable is of type Person but it is pointing to an object of Employee type, so person.GetType() returns Employee, not Person.
  6. The disadvantage of upcast: Upcasting brings loss, person reference variable cannot access Name or any field that belongs exclusively to Employee type. This is why person.Name = "Error when accessed Name"; throws error.

Downcasting w.r.t. Reference Types

There are two important points to remember about Downcasting w.r.t. Reference Types.
  1. An instance/object of Upper reference type cannot be assigned to Lower reference type without cast. It will throw error during compilation.
  2. A Lower reference type object that has be upcasted can be downcasted without any compile time error.
Now we see the first case.

CASE I

1. Downcasting Throwing Error At Compile time

class Program
{
    static void Main(string[] args)
    {

        Employee employee = new Person();
    }

}
class Person
{
    public int Id { get; set; }
}
class Employee : Person
{
    public string Name { get; set; } = "Unknown";
}
The compiler throws error message as follows: "Cannot implicitly convert type 'Person' to 'Employee'. An explicit conversion exists (are you missing a cast?)

2. Downcasting Throwing Error At Runtime

To hide this error, we can cast the person object i.e. new Person() to Employee type. 
class Program
{
    static void Main(string[] args)
    {

        Employee employee = (Employee)new Person();
    }

}
class Person
{
    public int Id { get; set; }
}
class Employee : Person
{
    public string Name { get; set; } = "Unknown";
}
This is a command to compiler that the person object is of Employee type. But if this type conversion is not correct then at runtime, there is error message: "Unhandled exception. System.InvalidCastException: Unable to cast object of type 'Person' to type 'Employee'." 3. 

CASE II

3. Downcasting Without Errors

A Lower reference type object that has be upcasted can be downcasted without any compile time error. Look at the following example in this regard:
class Program
{
    static void Main(string[] args)
    {

        // Lower reference type employee1 object upcasted to Person type
        // person is upcasted variable
        Person person = new Employee { Id = 1, Name = "Ajeet" };
        //Console.WriteLine(person.Name); // Error after upcast
        Console.WriteLine(person.GetType()); // Employee
        // Downcast upcasted person variable
        Employee employee = (Employee)person;
        Console.WriteLine(employee.Id);
        Console.WriteLine(employee.Name);
    }

}
class Person
{
    public int Id { get; set; }
}
class Employee : Person
{
    public string Name { get; set; } = "Unknown";
}

Wednesday, June 10, 2026

C# Example of Indexer in Generic class

Wrong Question: How can we decide whether an Indexer is generic -by looking at its index paramter or by looking at its return type? If paramter is generic type placeholder then generic or if return type is generic type placeholder then generic?

Right Answer: An indexer itself is not declared as "generic" in C#. Instead, an indexer can use generic type parameters that belong to its containing class or to the types used in its signature.

How do we identify that an indexer is using a generic type?

Look at both the parameter list and the value type (return type/get-set type). If any part of the indexer's signature uses a generic type parameter (T, TKey, TValue, etc.), then the indexer depends on a generic type parameter.

Case1. Generic type in the value type
public T this[int index]  // Generic because value type is T
{
    get; set;
}
Case2. Generic type in the index parameter
public string this[T key] // Generic because parameter type is T
{
    get; set;
}
Case3. Generic type in both places
public T this[T key] // Generic because both use T
{
    get; set;
}
The following code explains about Indexer in a Generic class:
public class Customer<T>
{
    private T Property1;
    private T Property2;
    public T this[int index]
    {
        get
        {
            if (index == 0)
                return Property1;
            else if (index == 1)
                return Property2;
            else
                throw new IndexOutOfRangeException();
        }
        set
        {
            if (index == 0)
                Property1 = value;
            else if (index == 1)
                Property2 = value;
            else
                throw new IndexOutOfRangeException();
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        Customer<string> customer = new Customer<string>();
        customer[0] = "Ajeet";
        customer[1] = "Delhi";
        Console.WriteLine("Customer " + customer[0] + " lives in " + customer[1]);
        Customer<int> customerObj = new Customer<int>();
        customerObj[0] = 101;
        customerObj[1] = 20000;
        Console.WriteLine("Customer ID: " + customerObj[0] + " has Ordered the products of value: Rs." + customerObj[1]);
    }
}
OUTPUT
Customer Ajeet lives in Delhi
Customer ID: 101 has Ordered the products of value: Rs.20000

Hot Topics