Working with generics and constraints in C#

By | March 12, 2017

I’ve seen many posts about generics in C# lately. And while they cover basics pretty well, most of them doesn’t contain information about constraints which are things that causing generics to be really fun.

101

Lets start with basics in case you’re not familiar with generics at all. When you think about generics in C#, one of first things you should think of are collections. For example let’s take dictionary, because it does have two generic types. Class definition for basic dictionary in C# looks like that.

Our main concern are values between angle brackets called TKey and TValue. Long story short, because I don’t want to bore you with basics, they are kind of placeholders for types. You could create dictionary with int, string, long or even some complex type(anything you want) as a key and you could use anything as a value too. Doing that mens you can store pretty much anything in collection but you can’t do anything but Dictionary stuff inside of it because it doesn’t know anything about stored objects, their fields and/or methods.

Interfaces and base classes

One of the basic constraint you can introduce to your generic classes are interface constraints along with base classes constraint. They’re ensuring that your T placeholder will implement interface or derive from base class that we provide in constraint (you’ll see how in a minute).

But without any contraint we could just put ANYTHING in T. Why should we event consider using constraints and making our generic class/method less useful? Basic reason for using constraints in our code is telling our generic class/method what can we do with our T. By constraining our generic TList to implement interface IList<TElements> we’re telling “you can do IList interface stuff on TList you’ll receive” to our code.

Let’s see some code then. Our scenario is pretty simple. We have ICanValidate interface in out code, it have one geter called IsValid that (wait for it!) … validates objects. We want to write extension method for ICollection interface that will add our ICanValidate objects to said collection only after validation returns true and returns said object or null depending on validation and adding result.

This is pretty simple but… code above does not compile. Why?

Class/Struct

Our method want to return null while returning T which could be anything at all that implements interface ICanValidate. For example one of those two types:

And because TypeB is a struct and just can’t be null. Our generic method will not compile because it still doesn’t know if it will be possible to return null.

But we have two solutions for problems like that. First is using class/struct contraint and adding it to our existing interface contraint. IMPORTANT: class/struct constraints must go at the beggining of our constraints. Our method signature would look just like that:

But what about TypeB now? Do we need to write another method for structs implementing our interface? Well, I don’t know what you’re trying to achieve but if you would want to return or whatever default value your struct have you could return default(T) in your method. It provides null for reference types or default values for value types.

New()

But let’s say we changed our mind and just don’t want to return default(T) anymore. Instead we want to return new instance of T. And there is constraint for that called new(). It constrains our T to have public parameterless constructor which we can use in our generic method and needs to be inserted last in our list of constraints. Just like that:

But what if we need parameters to create new instance of our object? For example we want to copy some values from old instance that didn’t pass validation? Well… we don’t have constraint for that but we can play with generics and interfaces a bit and create interface ICanRebuild that promises method From(T item) that will handle old object and extracts interesting values from it. I will not go into all the details but you could take a look at example below. It does work and I encourage you to analyze it a bit:

T : OtherT

There is one more type of constraint but I haven’t found any real uses for it yet. In msdn it’s called T : U but i don’t like this name, it’s not clear enough about what exactly U is. And is in fact just other generic parameter, name came alphabethicaly.

What this constraint do? It ensurest that T is derivded from other T. Like I’ve said, I didn’t found any real uses for that but it is possible to do this:

Why would you like to do something like that? I don’t know, but just remember it’s possible.

Summary

Generics are fun and they’re great for some truly universal extension methods and code reusability. If you didn’t knew much about constraints before reading above post I hope you’re proficient enough to make more possibilities for your methods/classes just by constraining type possibilities for them.

 

Podbij ↑