programming
C#
object-oriented
code-switching
type-determination

Using Case/Switch and GetType to determine the object

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When you need to branch on the runtime type of an object in C#, you have two common tools: pattern matching in switch or if expressions, and GetType(). They are not interchangeable. Pattern matching answers "is this object compatible with this type", while GetType() answers "what is the exact runtime type of this instance".

Prefer Pattern Matching for Normal Type Dispatch

Modern C# makes type-based branching easiest with pattern matching.

csharp
1object value = DateTime.UtcNow;
2
3string description = value switch
4{
5    int number => $"Integer: {number}",
6    string text => $"Text: {text}",
7    DateTime timestamp => $"Date: {timestamp:O}",
8    null => "Null value",
9    _ => $"Unhandled type: {value.GetType().Name}"
10};
11
12Console.WriteLine(description);

This style is usually better than manual GetType() checks because it gives you a correctly typed variable immediately and works naturally with inheritance.

GetType() Checks Exact Runtime Type

GetType() is useful when you really need an exact type comparison.

csharp
1object value = "hello";
2
3if (value.GetType() == typeof(string))
4{
5    Console.WriteLine("Exactly a string");
6}

That comparison is strict. It does not mean "is assignable to string-like interface". It means the runtime type is exactly System.String.

This distinction matters for inheritance:

csharp
1class Animal { }
2class Dog : Animal { }
3
4Animal animal = new Dog();
5
6Console.WriteLine(animal is Animal);                     // true
7Console.WriteLine(animal.GetType() == typeof(Animal));   // false
8Console.WriteLine(animal.GetType() == typeof(Dog));      // true

If your intent is polymorphic behavior, is or pattern matching is usually the right tool. If your intent is exact type identity, GetType() is appropriate.

Classic switch on the Type Object

If you want to inspect the type object explicitly, you can switch on GetType() as well:

csharp
1object value = 42;
2
3switch (value.GetType().Name)
4{
5    case "Int32":
6        Console.WriteLine("Integer");
7        break;
8    case "String":
9        Console.WriteLine("String");
10        break;
11    default:
12        Console.WriteLine("Something else");
13        break;
14}

This works, but it is usually less robust than pattern matching. Switching on type names introduces string comparisons and weakens compile-time help.

Use Polymorphism When the Behavior Belongs to the Object

Sometimes the best answer is not runtime type inspection at all. If each type knows how it should behave, virtual methods or interfaces are cleaner than a large switch.

csharp
1interface IShape
2{
3    double Area();
4}
5
6class Circle : IShape
7{
8    public double Radius { get; }
9    public Circle(double radius) => Radius = radius;
10    public double Area() => Math.PI * Radius * Radius;
11}
12
13class Rectangle : IShape
14{
15    public double Width { get; }
16    public double Height { get; }
17    public Rectangle(double width, double height)
18    {
19        Width = width;
20        Height = height;
21    }
22    public double Area() => Width * Height;
23}

In that design, consumers do not need switch or GetType() at all. They call Area() and let polymorphism handle the rest.

Common Pitfalls

The biggest mistake is calling GetType() on a possibly null reference. That throws immediately, while pattern matching can handle null safely in the same expression.

Another issue is using exact type comparison when inheritance should count as a match. GetType() == typeof(BaseType) fails for derived objects even when they are perfectly valid base-type instances.

People also sometimes build huge type switches where polymorphism or interfaces would be simpler and more maintainable. Runtime type checks are useful, but they are often a sign that behavior is living in the wrong place.

Finally, avoid switching on type names as strings unless you truly need display-oriented logic. It is more fragile than pattern matching and easier to break during refactoring.

Summary

  • Use pattern matching when you want normal runtime type dispatch in C#.
  • Use GetType() when you need an exact runtime type comparison.
  • Remember that GetType() does not treat derived types as matches for the base type.
  • Be careful with null when calling GetType().
  • If the behavior belongs to the type itself, polymorphism is often better than any type switch.

Course illustration
Course illustration

All Rights Reserved.