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 Ts 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
}