Simply, you declare a type or method with extra tags to indicate the generic bits:
class Foo<T> {
public Foo(T value) {
Value = value;
}
public T Value {get;private set;}
}
The above defines a generic type Foo
"of T
", where the T
is provided by the caller. By convention, generic type arguments start with T. If there is only one, T
is fine - otherwise name them all usefully: TSource
, TValue
, TListType
etc
Unlike C++ templates, .NET generics are provided by the runtime (not compiler tricks). For example:
Foo<int> foo = new Foo<int>(27);
All T
s have been replaced with int
in the above. If necessary, you can restrict generic arguments with constraints:
class Foo<T> where T : struct {}
Now Foo<string>
will refuse to compile - as string
is not a struct (value-type). Valid constraints are:
T : class // reference-type (class/interface/delegate)
T : struct // value-type except Nullable<T>
T : new() // has a public parameterless constructor
T : SomeClass // is SomeClass or inherited from SomeClass
T : ISomeInterface // implements ISomeInterface
Constraints can also involve other generic type arguments, for example:
T : IComparable<T> // or another type argument
You can have as many generic arguments as you need:
public struct KeyValuePair<TKey,TValue> {...}
Other things to note:
- static members etc are defined per generic type combination - so a static field on
Foo<int>
is separate to that on Foo<float>
.
- methods can be generic too - try to avoid using the same names as the class uses, as you won't be able to disambiguate
- nested types inherit the generic types from their parents
for example:
class Foo<T> {
class Bar<TInner> {} // is effectively Bar<T,TInner>, for the outer T
}