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: 42Box<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); }}
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 : IModelpublic 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) : Userget(long id) : Productget(long id) : Licenseget(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: AliceConsole.WriteLine(userService.Get(3)); // Output: (null of standaardwaarde)