Generics introduce to the .NET Framework the concept of type parameters, which make it possible to design classes and methods that defer the specification of one or more types until the class or method is declared and instantiated by client code.
In essence Generics are way of writing code that is:
- Without specific type
- Type-safe
- Reusable
Lets say for example that we want to display integer value and a message in our Console application. We can write standard Program class that uses DisplayIntValue class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class Program { static void Main(string[] args) { var intValue = new DisplayIntValue(3, "Thank you for displaying int value"); Console.WriteLine("{0}, result is: {1}", intValue.Message, intValue.InputValue); } } public class DisplayIntValue { public DisplayIntValue(int intValue, string message) { InputValue = intValue; Message = message; } public int InputValue { get; set; } public string Message { get; set; } } |
In future we receive request that our application must display also an double value and integer. What would be our options? We could create a new class DisplayDoubleValue that would be the same as DisplayIntValue but with a few tweaks, but that breaks DRY principle. This would be a good candidate to implement Generics.
We should make a copy of DisplayIntValue class and rename it to DisplayGenericValue. Then we change type of InputValue property and input parameter type intValue to T variable. T stands for generic type. Note that we get error: The type or namespace name ‘T’ could not be found (are you missing a using directive or an assembly reference?). You get this error because T variable is not defined in class signature. So next step will be to add <T> in your class signature. Your class signature should look like this now: public class DisplayGenericValue<T>. Lets try out our new code by displaying string or double value.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
static void Main(string[] args) { var doubleValue = new DisplayGenericValue<double>(3.1416, "Thank you for displaying double value"); Console.WriteLine("{0}, result is: {1}", doubleValue.Message, doubleValue.InputValue); var stringValue = new DisplayGenericValue<string>("Hi!", "Thank you for displaying string value"); Console.WriteLine("{0}, result is: {1}", stringValue.Message, stringValue.InputValue); } public class DisplayGenericValue<T> { public DisplayGenericValue(T genericValue, string message) { InputValue = genericValue; Message = message; } public T InputValue { get; set; } public string Message { get; set; } } |
Note that you had to specify type while creating instance of DisplayGenericValue class. By doing so, our generics class knew of what type or T variable will be.
FAQ
Q: Can i use multiple generic types?
A: Yes you can, but remember to add them first to class signature. Also, for readability sake naming convention for generic types goes like this: capital T followed by description. For example TMessage.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
static void Main(string[] args) { var twoIntValues = new DisplayGenericValue<int, int>(2016, 1337); Console.WriteLine("{0}, result is: {1}", twoIntValues.Message, twoIntValues.InputValue); } public class DisplayGenericValue<TInput, TMessage> { public DisplayGenericValue(TInput genericValue, TMessage message) { InputValue = genericValue; Message = message; } public TInput InputValue { get; set; } public TMessage Message { get; set; } } } |
Q: Can i create an generic method?
A: Of course you can. Make sure that return type of method is an generic variable and that your method signature contains generic variable.
1 2 3 4 5 |
public TSomething DoSomething<TSomething>(TSomething parameter) { //Do some work return parameter; } |
Note that if you define variable in class signature you do not have to specify type in Method signature.
1 2 3 4 5 6 7 8 |
public class DoSomethingClass<TSomething> { public TSomething DoSomething(TSomething parameter) { //Do some work return parameter; } } |
Q: I want to set limit types that can consume my generics class or method. How can i make it happen?
A: In your class/method signature you can add combination of following lines:
- where T: struct – limits types to reference types.
- where T : class – limits types to reference types.
- where T : new() – limits types to classes that have public parameterless constructor.
- where T : <base class name> – limits types to specified class or class that derives from that class.
- where T : <interface name> – limits types to specified interface or interface that inherits that interface.
- where T : U – the type argument supplied for T must be or derive from the argument supplied for U.
1 2 3 4 5 6 7 |
public class MyClass<T> where T : struct public class MyClass<T> where T : BaseClass public class MyClass<T> where T : BaseClass, IInterface, new() public class MyClass<T, V> where T : struct where V : BaseClass, IInterface |