Skip to content

Unbound generic types and `nameof` C# 14.0correctness

Use the `nameof` keyword with generic definitions.

The nameof operator C# 6.0 previously could only be used with generic types when all type arguments were specified. This meant you couldn't get the name of a generic type definition itself without providing type arguments, even when you didn't care about the specific type.

C# 14.0 allows nameof to work with unbound generic types using the <> syntax, making it possible to get the name of generic type definitions without needing to specify type arguments.

Code

C#
// Works with unbound generic types - no need to pick arbitrary type arguments
string listName = nameof(List<>);              // "List"
string dictName = nameof(Dictionary<,>);       // "Dictionary"

// Useful for logging and diagnostics
Console.WriteLine($"Registering {nameof(List<>)} handler");

// Works with custom generic types
class MyGeneric<T, U> { }
string myName = nameof(MyGeneric<,>);          // "MyGeneric"
C#
// Had to specify arbitrary type arguments even though they were irrelevant
string listName = nameof(List<int>);           // "List"
string dictName = nameof(Dictionary<int, string>); // "Dictionary"

// Misleading - suggests a dependency on those specific types
Console.WriteLine($"Registering {nameof(List<int>)} handler");

// Same issue with custom types
class MyGeneric<T, U> { }
string myName = nameof(MyGeneric<int, string>); // "MyGeneric"

Notes

  • Use <> for single type parameter, <,> for two, <,,> for three, etc.
  • Returns the simple name without arity suffix, same as the bound form (e.g. both nameof(List<>) and nameof(List<int>) return "List")
  • Aligns nameof syntax with typeof which already accepted unbound generic types
  • Avoids misleading code that specifies arbitrary type arguments just to satisfy the compiler

More information