Generic classes have type parameters. Separate classes, each with a different field type, can be replaced with a single generic class
.
A generic class
introduces a type parameter (often specified as the letter T). This becomes part of the class
definition itself. Generic methods can also be designed.
class
exampleTo start, we specify a generic type. These types have type parameters. When compiled, the type parameters refer to the type specified.
int
type parameter with the Test class
. The T is substituted with an int
.string
type parameter, and Test has a type of string
.using System; class Test<T> { T _value; public Test(T t) { // The field has the same type as the parameter. this._value = t; } public void Write() { Console.WriteLine(this._value); } } class Program { static void Main() { // Version 1: use int type parameter. Test<int> test1 = new Test<int>(5); // Call the Write method. test1.Write(); // Version 2: use string type parameter. Test<string> test2 = new Test<string>("cat"); test2.Write(); } }5 cat
Generic methods have type parameters. They provide a way to parameterize the types used in a method. One implementation is used with many different types.
TValue
or similar.GetInitializedList
is a generic method that constructs a special List
. It uses a type parameter with name T.GetInitializedList
method is also a value of type T.using System; using System.Collections.Generic; class Program { static List<T> GetInitializedList<T>(T value, int count) { // This generic method returns a List with ten elements initialized. // ... It uses a type parameter. // ... It uses the "open type" T. List<T> list = new List<T>(); for (int i = 0; i < count; i++) { list.Add(value); } return list; } static void Main() { // Use the generic method. // ... Specifying the type parameter is optional here. // ... Then print the results. List<bool> list1 = GetInitializedList(true, 5); List<string> list2 = GetInitializedList<string>("Perls", 3); foreach (bool value in list1) { Console.WriteLine(value); } foreach (string value in list2) { Console.WriteLine(value); } } }True True True True True Perls Perls Perls
class
constraintsThe C# language also provides ways for you to add more features to your generic types by reducing the range of types they can be parameterized with.
class
requires that its type parameter implement IDisposable
. We can thus call the Dispose
method or using statement.struct
. Ints are structs so you could use Python int
as the constructed type.class
). It also requires a public parameterless constructor.using System; using System.Data; /// <summary> /// Requires type parameter that implements interface IDisposable. /// </summary> class Ruby<T> where T : IDisposable { } /// <summary> /// Requires type parameter that is a struct. /// </summary> class Python<T> where T : struct { } /// <summary> /// Requires type parameter that is a reference type with a constructor. /// </summary> class Perl<V> where V : class, new() { } class Program { static void Main() { // DataTable implements IDisposable so it can be used with Ruby. Ruby<DataTable> ruby = new Ruby<DataTable>(); // Int is a struct (ValueType) so it can be used with Python. Python<int> python = new Python<int>(); // Program is a class with a parameterless constructor (implicit) // ... so it can be used with Perl. Perl<Program> perl = new Perl<Program>(); } }
Generic constraints are not often useful. If you are developing a complex framework (like the .NET Framework itself) they are necessary.
IndexOf
A common generic method that is not part of a generic type is the Array.IndexOf
method. We usually can omit the type parameter when calling it.
Usually we will use generic types and methods that are provided as part of .NET. But we can create our own, and this is beneficial in certain programs.