Generics
Value of Generics
In the past, if we wanted to create a custom List
of, say, Book
instances, you had to create a dedicated BookList
class:
One way around this was to create a more reusable ObjectList
, which would box value types or cast classes that you use.
However, this has performance costs due to boxing and casting!
So, generics were created to solve these problems! With generics, you create a class once and defer the typing until runtime.
For multiple types, a good practice is to name the types:
Pro tip: In most cases, you will find yourself using generics that are part of .NET. It's very, very rare that you'll ever need to create your own generics.
In .NET, all the generics can be found in System.Collection.Generic.XXXXX
.
Applying Constraints to Accepted Types
It's valuable to be able to constrain the accepted types that can be passed in as T
for two reasons:
By default, a user could pass in any
T
type they want, which could be too wild.If you don't constrain the accepted types, C# will assume
T
is an object, so C# will error out when you try to perform invalid operations or invoke invalid methods that objects don't have.
Suppose you're creating a generic class with a Max
method that finds the max between 2 inputs:
The operation a > b
won't compile because C# will consider that an invalid operation. Additionally, what happens if the user passes in a string? We wouldn't want that.
So, one solution to this problem is to constrain T
to an interface, setting a requirement that the type matches a specific contract.
In our case, we can constrain T
to an IComparable
, giving us access to the CompareTo
method:
Note: You're not limited to interfaces as constraints though. Here's some more.
For example, here's a constraint for a class:
Here's another example constraint for a struct:
Last updated