Introduction to generics in C# Part 1
March 1, 2016 Leave a comment
Introduction
Generics is a way to generalise your code in .NET. We can generalise the types that an object and/or a function operates on. With generics we can reuse the same portion of code with multiple types. If used in appropriate places it can be a great tool against code duplication.
Generics is nothing new, it was a new language feature in .NET 2.0.
First example with ID types
Imagine that you’re modeling the domain of your business: Customer, Order, Car, Book etc. It is quite normal that many domain objects will have a unique identifier for tracking purposes: the Customer with ID 5321, the Order with ID 77cd0ecc-583d-48e8-97e6-4fd81a7ff6ef or the Car with ID gdGRds432Fsrt. The examples show that an ID can be of different types. The examples show integer, GUID and string IDs but there are probably other ID types out there.
Since our domain objects will have an ID field we can introduce an abstract base class with the ID property. However, which type should it be? Let’s pretend that we don’t know generics at all. Our first attempt to solve the problem is to introduce 3 base classes with one implementing class each:
public abstract class IntegerIdDomain { public int Id { get; } public IntegerIdDomain(int id) { Id = id; } } public abstract class StringIdDomain { public string Id { get; } public StringIdDomain(string id) { Id = id; } } public abstract class GuidIdDomain { public Guid Id { get; } public GuidIdDomain(Guid id) { Id = id; } } public class Customer : IntegerIdDomain { public Customer(int id, string name) : base(id) { Name = name; } public string Name { get; } } public class Order : GuidIdDomain { public Order(Guid id, string address) : base(id) { Address = address; } public string Address { get; } } public class Shipment : StringIdDomain { public Shipment(string id, string meansOfTransportation) : base(id) { MeansOfTransportation = meansOfTransportation; } public string MeansOfTransportation { get; } }
So we have 3 domain objects with 3 different ID types.
The objects are all domains but still that’s not fully represented in code.
Fortunately for us there is generics! All we need to introduce is a type parameter in an abstract base class for all domains. A single type parameter can be indicated with any name really but the convention is to use ‘T’. Let’s see what the Domain class could look like:
public abstract class Domain<T> { public T Id { get; } public Domain(T id) { Id = id; } }
We put the type parameter between those diamond shaped brackets. You can think if the type parameter as a placeholder for the actual type. It can be any object: string, int, Guid, List, a custom object, whatever. The main thing is that all references to T within that class will then be of the declared type.
Let’s see how our domain classes can be restructured:
public class Customer : Domain<int> { public Customer(int id, string name) : base(id) { Name = name; } public string Name { get; } } public class Order : Domain<Guid> { public Order(Guid id, string address) : base(id) { Address = address; } public string Address { get; } } public class Shipment : Domain<string> { public Shipment(string id, string meansOfTransportation) : base(id) { MeansOfTransportation = meansOfTransportation; } public string MeansOfTransportation { get; } }
We provide the type parameter for the ID when we declare that each domain object derives from the generic Domain object.
That’s probably as simple as it gets with generics. We’ll continue with other examples in the next post.
View all various C# language feature related posts here.