There are two forms of generics in C#:
Terminology
The JIT creates different code for each constructed generic type with a value type argument but it shares the generated code for all the constructed types that use a reference type as the type argument.
Generic methods are usually used with non-generic types. For example, the Array class has a bunch of generic methods:
public static int BinarySearch<T>(T[] array, T value); public static int BinarySearch<T>(T[] array, T value, IComparer<T> comparer); public static TOutput[] ConvertAll<TInput, TOutput>(TInput[] array, Converter<TInput, TOutput> converter); public static bool Exists<T>(T[] array, Predicate<T> match); ...
Generic classes also can contain generic methods. For example, the List<T> class has a generic method ConvertAll<U>:
// T is the type parameter of the List<T> class // TOutput is the type parameter of the generic method ConvertAll<U> public List<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter); // A concrete type for converting a list of strings to a list of doubles could look like this: public List<double> ConvertAll<double>(Converter<string, double> converter);
Example: Convert strings to doubles using a List<T>'s generic method ConvertAll<U>:
// Create and populated a list of strings. var list = new List<string> { "8.34", "2.1", "2341.889" }; // Call a generic method ConvertAll. List<double> doubles = list.ConvertAll<double>(s => Convert.ToDouble(s));
Type constraints give you more control over which type arguments are considered valid when creating custom generic types and methods. Thanks to that you can call methods on instances of the type parameter, or create new instances, or make sure you only accept reference types.
Types of constraints:
Example:
class MyBaseClass {} interface Interface1 {} // MyClass<T,U> requires T to derive from MyBaseClass and implement Interface1. // It also requires U to provide a parameterless constructor. class MyClass<T,U> where T : MyBaseClass, Interface1 where U : new() {...}
Example: Conversion type constraints:
class MyClass<T> where T : Stream { ... } // identity conversion MyClass<Stream> struct MyClass<T> where T : IDisposable { ... } // reference conversion MyClass<SqlConnection> class MyClass<T> where T : IComparable<T> { ... } // boxing conversion MyClass<double> // type parameter constraint class MyClass<T,U> where T : U { ... } // reference conversion MyClass<Stream,IDisposable>
Example: Combine constraints:
class Sample<T> where T : class, IDisposable, new() class Sample<T> where T : struct, IDisposable // Each list of type parameter constraints needs its own introductory where. class Sample<T,U> where T : Stream where U : IDisposable // Although U is a value type it can derive from T (which is a reference type) when T is an object // or an interface that U implements. class Sample<T,U> where T : class where U : struct, T
Example: A helper class to store user's settings in the UWP's local storage:
class SettingsHelper { public int CurrentUserId { get { return GetValue<int>("CurrentUserId"); } set { SetValue("CurrentUserId", value); } } private T GetValue<T>(string key) { ApplicationDataContainer settings = ApplicationData.Current.LocalSettings; if (settings.Values.ContainsKey(key)) { return (T)settings.Values[key]; } else return default(T); } private void SetValue<T>(string key, T val) { T currentValue = GetValue<T>(key); if (!EqualityComparer<T>.Default.Equals(currentValue, val)) { ApplicationDataContainer settings = ApplicationData.Current.LocalSettings; settings.Values[key] = val; } } }
Each closed type has its own set of static fields. The same applies for static initializers and static constructors:
class MyCounter<T> { public static int Count; } ... Console.WriteLine(++MyCounter<int>.Count); // 1 Console.WriteLine(++MyCounter<int>.Count); // 2 Console.WriteLine(++MyCounter<string>.Count); // 1 Console.WriteLine(++MyCounter<object>.Count); // 1
For types with multiple generic parameters each different list of type arguments counts as a different closed type.
For information on using reflection with generics refer to the Generics subsection of the Reflection notes.