Generics
Generics let you write a class or method once and use it with any type, without losing type checks. Follow one generic class, Box<T>, as Program.cs uses it twice: the type arguments int and string replace the type parameter T, the compiler makes the constructed types Box<int> and Box<string>, catches wrong types before anything runs, and at run time the program prints 42 hi.
5 quick questions
Pick an answer to see why. Your score saves with your progress.
01In Box<string> b = new("hi");, what is string?
T is the type parameter, declared in class Box<T>. The type you supply when you use the class, here string, is the type argument. Together they make the constructed type Box<string>.
02a is a Box<int>. What happens with the line a.Value = "oops";?
In Box<int>, T is int, so Value is an int. The compiler checks every use before anything runs and rejects a string with CS0029.
03Why does int n = a.Value + 1; need no cast, when a box of object would need (int)box.Value?
The type argument int replaces T, so the compiler knows a.Value is an int. With object, the compiler only knows it is some object, so you must cast, and a wrong cast fails only at run time.
04Box<T> is declared with where T : struct. Which line fails to build?
where T : struct only allows non-nullable value types. int and double are value types; string is a reference type, so Box<string> fails with error CS0453.
05How does the CLR run Box<int> and Box<string>?
The assembly holds Box<T> once, as IL. The CLR makes specialised code for each value type used, and one shared version for all reference types, since every reference is the same size.
Glossary
- Generic type
- A class, struct, interface or delegate written with one or more type parameters, such as Box<T> or List<T>.
- Type parameter
- A placeholder for a type, such as T in class Box<T>. Inside the definition it is used like a real type.
- Type argument
- The actual type supplied when a generic type is used, such as int in Box<int>.
- Constructed type
- A generic type with its type arguments filled in, such as Box<int> or Box<string>.
- Constraint
- A rule after where that limits which types may replace T, such as where T : struct or where T : IComparable<T>.
- Cast
- Code that converts a value to another type, such as (int)box.Value. A wrong cast throws an InvalidCastException at run time.
- Boxing
- Wrapping a value type such as int inside an object on the heap. Generic code with a value type argument avoids it.
Notes and sources
- The program is real C# 12 (.NET 8 or later): the five statements, then the class, all in Program.cs. dotnet run prints 42 hi.
- class Box<T>(T v) uses a primary constructor (C# 12). The older form is a constructor written inside the class: public Box(T v) { Value = v; }.
- b.Value holds a reference to the string object "hi", shown here as its text. n and s are local variables, not on the heap; the figure puts them in one memory row for clarity.
- The CLR shares one copy of machine code among reference types because every reference is the same size. Each value type gets its own copy, so Box<int> stores and reads an int directly, without boxing.
- Console works without a "using System;" line because new console projects turn on implicit usings (.NET 6 and later).
- Microsoft Learn: Generic types and methods (C# fundamentals)
- Microsoft Learn: Generic type parameters
- Microsoft Learn: Generic classes
- Microsoft Learn: Constraints on type parameters
- Microsoft Learn: Generics in the runtime
- Microsoft Learn: Generics in .NET
- Microsoft Learn: Boxing and unboxing
- Microsoft Learn: Primary constructors (C# 12)
- Microsoft Learn: Compiler error CS0029
- Microsoft Learn: String interpolation
Quiz Unlocks after the last step.