Wat zijn generics?

Een generic in C# is een manier om typeparameters te gebruiken in klassen, interfaces en methoden, zodat ze kunnen werken met verschillende datatypes zonder dat je meerdere versies van de code hoeft te schrijven. Dit verhoogt de herbruikbaarheid en typeveiligheid van je code. Een voorbeeld hiervan is een list (List<T>). Dus bij de instantie van de lijst wordt bepaald welk type de lijst kan bevatten.

Casus

Om te voorkomen dat een ontwikkelteam verschillende implementaties moet schrijven voor datastructuren wordt er gebruik gemaakt van de standaard implementatie van C#, zoals:

  • List<T>
  • Queue<T>
  • Dictionary<T>

Hoe zit een generic in elkaar?

Generics kunnen op klasse en interface niveau worden gedeclareerd: class Box<T> of interface IVoertuig<T>. En op methode niveau: void Print<T>(T value). Hierna volgen voorbeelden. Tenslotte voegen we contraints toe aan T, bijvoorbeeld: T moet interface IDier implementeren.

Codevoorbeeld generic klasse

public class Box<T>  // T is een typeparameter
{
    private T _item;
 
    public void Add(T item)
    {
        _item = item;
    }
 
    public T GetItem()
    {
        return _item;
    }
}

Gebruik:

Box<int> intBox = new Box<int>();
intBox.Add(42);
Console.WriteLine(intBox.GetItem()); // Output: 42
 
Box<string> stringBox = new Box<string>();
stringBox.Add("Hallo");
Console.WriteLine(stringBox.GetItem()); // Output: Hallo

Codevoorbeeld generic interfaces

Interfaces kunnen ook generiek zijn:

public interface IRepository<T>
{
    void Add(T item);
    T Get(int id);
}

Implementatie:

public class UserRepository : IRepository<string>
{
    private Dictionary<int, string> _users = new();
 
    public void Add(string user) => _users[_users.Count + 1] = user;
 
    public string Get(int id) => _users.ContainsKey(id) ? _users[id] : "Niet gevonden";
}

Codevoorbeeld generic methode

Je kunt ook generieke methoden maken zonder dat de hele klasse generiek is:

public class Utility
{
    public static void Print<T>(T value)
    {
        Console.WriteLine(value);
    }
}

Gebruik:

Utility.Print<int>(10);   // Output: 10
Utility.Print("Hallo");   // Output: Hallo (type wordt automatisch afgeleid)

Codevoorbeeld generic type constraints

Voor het type T in de interface interface IRepository<T> kan een beperking worden toegevoegd dan T een property Id moet bezitten.

interface IModel 
{
	Id: long
}
 
interface IRepository<T> where T:IModel
{
 ...
}

Gebruik:

public class User : IModel
 
public class UserRepository : IRepository<User>

Hoe gebruik je generics?

Generics pas je in de eerste plaats toe bij het gebruik van collecties in C#. Ook andere technieken maken standaard gebruik van generics zoals LINQ query syntax en LINQ method syntax. Als je vaker ongeveer dezelfde code schrijft waarbij alleen het gebruikte type verschilt, dan is het een overweging waard om generieke oplossing te ontwikkelen.

Casus

Een ontwikkelteam maakt gebruik van IRepository<T>. De codebase bevat een klasse Service . Na twee sprints begint het op te vallen dat de ontwikkelde services een aantal methoden hebben die steeds nagenoeg het zelfde zijn, waaronder: get(long id) : User get(long id) : Product get(long id) : License get(long id) : Invoices Het ontwikkelteam heeft een generieke klasse ontwikkeld: Service<T>. Er is gekozen voor declaratie van het type op klasseniveau. De reden hiervoor lag in het feit dat het type in meerdere methoden werd gebruikt. Hiermee is de Service klasse herbruikbaar geworden. Het ontwikkelteam overweegt nog om een type constraint toe te voegen.

Mogelijke uitwerking van de casus

public class Service<T>  
{
    private Dictionary<int, T> _items = new();
 
    public void Add(int id, T item)
    {
        _items[id] = item;
    }
 
    public T Get(int id)
    {
        return _items.ContainsKey(id) ? _items[id] : default!;
    }
}
 
Service<string> userService = new Service<string>();
userService.Add(1, "Alice");
userService.Add(2, "Bob");
 
Console.WriteLine(userService.Get(1)); // Output: Alice
Console.WriteLine(userService.Get(3)); // Output: (null of standaardwaarde) 

Volgende stap: Oefeningen generics