0

Are there any real-world examples of using subtyping constraints on type parameters in .NET generics? By «subtyping constraints» I mean

where T : <base class name>

and

where T : U

May be there are some standard generics with corresponding constraints? Or specialized generic .NET-libraries.

UPD There are lots of good examples with interface constraints

where T : <interface name>

But subtyping constraints seems to be very specific and not so useful. I try to understand, in which cases this kind of constraints is really crucial. Luaan's answer contains examples with where T : <base class name> from ASP.NET MVC, but I am still interested in real-world examples with where T : U constraint.

juliet
  • 193
  • 6
  • 1
    Good enough example? `Nullable where T : struct` – Atomosk Jun 19 '14 at 07:53
  • 1
    Stackoverflow is typically for answering real problems. If you have trouble with generics explain what you did and what you really wanted. – jgauffin Jun 19 '14 at 08:02
  • No, I don't mean `T : struct` or `T : class`, but exactly subtyping constraints. Subtyping constraints are supported in .NET, but it seems that interface constraints `where T : ` are more actively used (and it's not difficult to find good examples). But I can not guess complex and interesting real-world examples with subtyping constraints, are they really useful? – juliet Jun 19 '14 at 08:46
  • Yeah, because interfaces are less limiting. Even if you do use a class rather than an interface, you'd probably want it to be an abstract class. But really, the basic idea is that the generic type constraint *should* be a contract - in other words, an interface. So using a class instead of an interface is IMO just a thing to save you writing an interface for that abstract class. – Luaan Jun 19 '14 at 09:13

3 Answers3

6

I use them for a repository class in my data access layer e.g.

public interface IRepository<T>
    where T : IDbItentity
{

    IList<T> GetAll();
    T GetById(int id);
    int Insert(T saveThis);
    void Update(T updateThis);
    void Delete(T deleteThis);
}

Where IDBItentity is an interface as follows:

public interface IDbItentity
{
    int Id { get; }
}
Jon Hunter
  • 882
  • 2
  • 8
  • 18
3

It's something you use a lot. In a way, it immitates the way normal inheritance works.

So for example, if you've got a common functionality built around O/RM entities, you can just create an entity base class, and use that as a type constraint in all the various data layers manipulating that entity.

Extremely useful is its use with interfaces.

And very often, you're going to write some kind of a wrapper around something else.

The basic idea is that you use those when you really only want the type parameter to fit some use case, but rather than just using the interface, you let the user of your code supply their concrete type. It still implements all the stuff you need to work correctly, but at the same time, the user can use all the features, even those you don't know about.

You will not find many cases in the BCL. Basically, this has to do with the fact that type constraints are constraints. The BCL usually uses generic types and methods to write very general functionality - I guess that's in part because of the fact that most of the BCL was there before generics, and because most of the time, inheritance will work just as well, if not better.

There's still differences, though. Say you need a collection of some entities. If you just use List<Entity>, you're saying "I expect any entity whatsoever, thanks". If you use List<T> where T : Entity (pseudocode), you're saying "I need to know the type you're giving me is an Entity, but I only want one kind of entity in the whole collection".

All in all, if you want good applications of generic type constraints, look at newer code. For example, in ASP.NET MVC, there's things like this:

public abstract class CachedAssociatedMetadataProvider<TModelMetadata> 
       : AssociatedMetadataProvider 
       where TModelMetadata : ModelMetadata

public class DataAnnotationsModelValidator<TAttribute>
       : DataAnnotationsModelValidator 
       where TAttribute : ValidationAttribute

It's also very useful when you're using Actions (or events) to tag functionality to some such general class from the outside.

Again, the uses are basically such:

  • Constraint the types that can be used in your class
  • Ensure the type passed to you conforms to some contract
  • Improve useability for the users of your code
  • Performance optimization of value-types, mostly avoiding boxing - e.g. you can use IComparable without having to box the value
Luaan
  • 62,244
  • 7
  • 97
  • 116
  • Thanks, I like ASP.NET MVC example. Interface constraints seem to be more useful, that is why I search examples with subtyping constraints exactly. – juliet Jun 19 '14 at 08:55
  • But what about real examples with `where T : U` (`T, U` — type parameters)? – juliet Jun 19 '14 at 08:59
  • Well, that doesn't sound very useful - if you've got a constraint like that, why are you declaring a new separate type parameter? I can think of a few (say, a method that copies values of `U` to `T`, which is a descendant of `U`), but it's not something I've used before. Also, `where T : Something` is a bit more useful. For example, you could have a wrapper type around a type, and it could have a method that takes some of `U` from a `List`. And perhaps you'd like a collections of those lists for some reason. Why not simply write `List` in the first place, though? – Luaan Jun 19 '14 at 09:10
  • Again, if `Something` is interface, it's ok (for instance, `where G : IGraph`), but with classes... – juliet Jun 19 '14 at 09:18
  • 1
    It's a shortcut. The idea is that you want to restrict the type parameter to a certain contract. That is done by using an interface. If you don't want to create an interface just to expose the contract of a class, you simply use the class directly instead. It's more closed, but the principle is exactly the same. When you're working on internal stuff, it may simply be that doing the same stuff using interfaces would be more work with no benefit. And just as importantly, there's a useful thing that interfaces don't allow you to do - create instances. I can do `new G(whatever)`. – Luaan Jun 19 '14 at 09:33
  • Sure, you can use factories and what-not, but it adds more and more complexity. If you don't need the openness, it's unnecessary complexity. – Luaan Jun 19 '14 at 09:34
  • I didn't catch the issue about creating of instances: to create instance of type parameter `T` I need a constraint `where T : new()`. If I have `where T : SomeClass` (and `SomeClass` has the constructor `SomeClass(data)`) it doesn't mean that I can create instance `new T(data)` as far as constructors are not inherited. – juliet Jun 19 '14 at 10:04
  • Thank you for idea of simplifying contracts. Indeed, sometimes you have single class implementing the interface, and interface in that case may be redundant. – juliet Jun 19 '14 at 10:07
  • Ah, you're right, I made a simple mistake in my test. Well, it still at least allows you to create an instance of the base class, but that isn't much of a help, really. – Luaan Jun 19 '14 at 13:28
0

Often it is used for extension methods, for example:

   public static string Parse<Tenum>(this object spr, int id) where Tenum : struct, IConvertible 
   {
       Contract.Ensures(typeof(Tenum).IsEnum, "type must be is enum");
       return ((Tenum)(object)id).ToString();

   }

 string ProductTypeTitle =  this.Parse<ProductType>(product.ProductTypeID);
Ilya Sulimanov
  • 7,636
  • 6
  • 47
  • 68
  • Again, it's is not _subtyping_ constraint. I mean that right side of constraint must be a class or another type parameter. – juliet Jun 19 '14 at 08:48