C#
Generics
Programming
Software Development
Types

Generics in C, using type of a variable as parameter

Master System Design with Codemia

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

Introduction

In C#, generic type arguments are chosen at compile time, not extracted from an arbitrary variable at runtime. That is why you cannot directly write code that says "take the type of this variable and plug it into a generic parameter" unless the compiler already knows the type or you drop down to reflection.

Generics Need Compile-Time Types

This works because the compiler knows the type argument:

csharp
1using System;
2
3static T Echo<T>(T value)
4{
5    return value;
6}
7
8Console.WriteLine(Echo<int>(42));
9Console.WriteLine(Echo("hello"));

In the second call, the compiler infers T from the argument type.

What C# does not let you do is treat a Type value as if it were a generic type argument in normal syntax.

typeof(x) Is Not a Generic Argument

People often want something like this:

csharp
Type t = someObject.GetType();
// Not valid C#
// var result = Echo<t>(someObject);

That cannot work in normal generic syntax because t is a runtime value, while Echo<t> requires a compile-time type parameter.

This is the core rule to remember:

  • generic syntax uses types known to the compiler
  • 'System.Type values exist at runtime'

Those are related concepts, but they are not interchangeable in C# syntax.

Let the Compiler Infer the Generic Type

If you already have a value and you are calling a generic method directly, type inference is usually enough.

csharp
1using System;
2
3static void PrintType<T>(T value)
4{
5    Console.WriteLine(typeof(T).Name);
6}
7
8object number = 42;
9
10PrintType(42);          // T is int
11PrintType("hello");     // T is string
12PrintType(number);      // T is object, because variable is typed as object

Notice the last line. The runtime object is an int, but the compile-time type of the variable is object, so inference chooses object.

Use Reflection When the Type Is Known Only at Runtime

If all you have is a runtime Type, reflection is the normal escape hatch.

csharp
1using System;
2using System.Reflection;
3
4class Demo
5{
6    public static void PrintGeneric<T>()
7    {
8        Console.WriteLine(typeof(T).FullName);
9    }
10}
11
12Type runtimeType = typeof(DateTime);
13MethodInfo method = typeof(Demo).GetMethod(nameof(Demo.PrintGeneric))!;
14MethodInfo closedMethod = method.MakeGenericMethod(runtimeType);
15closedMethod.Invoke(null, null);

This works, but it is slower, less readable, and more brittle than direct generic calls.

Sometimes Generics Are the Wrong Tool

If the real requirement is "handle values of different runtime types uniformly," then a non-generic design may be better:

  • interfaces
  • base classes
  • pattern matching
  • dictionaries keyed by Type

Example with pattern matching:

csharp
1using System;
2
3static void PrintValue(object value)
4{
5    switch (value)
6    {
7        case int i:
8            Console.WriteLine($"int: {i}");
9            break;
10        case string s:
11            Console.WriteLine($"string: {s}");
12            break;
13        default:
14            Console.WriteLine($"other: {value}");
15            break;
16    }
17}

This avoids pretending the problem is compile-time generic when it is really runtime dispatch.

Generic Types Can Still Use Type Values Indirectly

A common practical pattern is to map runtime Type objects to pre-built handlers.

csharp
1using System;
2using System.Collections.Generic;
3
4var handlers = new Dictionary<Type, Action<object>>
5{
6    [typeof(int)] = v => Console.WriteLine((int)v + 1),
7    [typeof(string)] = v => Console.WriteLine(((string)v).ToUpperInvariant())
8};
9
10object value = "hello";
11handlers[value.GetType()](value);

This is often simpler than reflection-heavy generic invocation.

Common Pitfalls

The biggest mistake is confusing a runtime Type instance with a compile-time generic argument. They are not interchangeable in C# syntax.

Another issue is relying on generic type inference when the variable has already been widened to object. In that case the compiler can only infer object.

A third problem is reaching for reflection immediately when pattern matching or an interface-based design would be clearer and safer.

Summary

  • Generic type parameters in C# must be known at compile time.
  • A runtime Type value cannot be used directly in normal generic syntax.
  • Let the compiler infer the generic type when possible.
  • Use reflection only when the type is truly known only at runtime.
  • If the problem is runtime dispatch, consider non-generic designs first.

Course illustration
Course illustration