C# provides generics to help us remove the need for casting, to improve type safety and make it easier to create generic classes and generic methods.
If you want to see complete navigation of this tutorial, you can do that here C# Intermediate Tutorial.
To download the source code, you can visit Generics in C# Source Code.
We are going to split this article into the following sections:
Generic Type T
To create a generic class, we need to provide a type between angle brackets:
public class CollectionInitializer<T> { ... }
The T
in this example acts as a placeholder for a type we want to work with. We need to provide that type once we instantiate this generic class. So let’s see this with a simple example:
public class CollectionInitializer<T> { private T[] collection; public CollectionInitializer(int collectionLength) { collection = new T[collectionLength]; } public void AddElementsToCollection(params T[]elements) { for(int i=0; i<elements.Length; i++) { collection[i] = elements[i]; } } public T[] RetrieveAllElements() { return collection; } public T RetreiveElementOnIndex(int index) { return collection[index]; } }
And to use this generic class:
class Program { static void Main(string[] args) { CollectionInitializer<int> initializer = new CollectionInitializer<int>(5); initializer.AddElementsToCollection(5, 8, 12, 74, 13); int[] collection = initializer.RetrieveAllElements(); int number = initializer.RetreiveElementOnIndex(3); foreach (int element in collection) { Console.WriteLine(element); } Console.WriteLine(); Console.WriteLine($"Element on the selected index is: {number}"); Console.ReadKey(); } }
As we can see in our CollectionInitializer
class, we need to provide the type which we want to work with. Then, we can just call the methods implemented within our generic class. Of course, we didn’t implement safety checks (if we send more elements than the array length is etc) just for a sake of simplicity. Now we can see the result:
A generic class can have more than one type parameter:
public class CollectionKeyValueInitializer<TKey, TValue>
Constraints with Generics
Sometimes, we want to ensure that just certain types can be invoked with our generic class. It is often useful while working with classes or interfaces. We can do that by using the where keyword:
public class CollectionInitializer<T> where T: Student
or we can limit our generic class to work only with classes:
public class CollectionInitializer<T> where T: class
There are different variations for this constraints, they depend on the situation we are working in. It is important to know that if we constraint our generic class to work only with classes, we will get an error if we provide value type. If we want to work only with value types, we can constraint our generic class like this:
public class CollectionInitializer<T> where T: struct
Generic Methods
In the same way that we can create a generic class, we can create a generic method. We just need to set a type parameter in angle brackets right behind a method name:
public void ExampleMethod<T>(T param1, T param2) { //Methods body }
We must pay attention to the type parameter identifier if our generic method exists inside a generic class. If that class has a type T then, our method needs to have a different type (U, Y, R…). Otherwise, the type T from a method will hide the type T from a class.
Conclusion
In this article, we have learned:
- How to use Generics in C#
- How to implement constraints in our generic classes
- The way to create generic methods
In the next article, we are going to talk about Queue, Stack, and Hashtable in C#.