On generics limitations

I wish generics in C# were more powerful. Why this is not legal?

public class ServiceHost<TService> : ServiceHost where TService : class
{
    public ServiceEndpoint AddServiceEndpoint<TServiceInterface>(Binding binding, string address)
        where TService : TServiceInterface
    {
        return AddServiceEndpoint(typeof(TServiceInterface), binding, address);
    }
}

Technorati Tags:

Comments

Paul Batum says:

Maybe I’m confused, but are you trying to restrict the AddServiceEndPoint generic parameter to be a supertype of TService (or exactly TService)? So for example this would be legal:

host.AddServiceEndpoint(null, null);

Because object is guaranteed to be a supertype of TService.

Paul Batum says:

Sigh, html and angle brackets at odds yet again. This is why a preview button for comments is useful! That line of code was supposed to be:

host.AddServiceEndpoint<object>(null, null);

You are absolutely right – this does not prevent you from calling it with System.Object as parameter. This is not a problem however in some cases.
In this particular one, it is, but still – in 99,9% cases it’s enough.
And to make it 100% accurate you’d have to be able to write:
public ServiceEndpoint AddServiceEndpoint(Binding binding, string address)
where TService : TServiceInterface
where TServiceInterface: interface
where typeof(TServiceInterface).GetCustomAttribute(ServiceContractAttribute)!=null
somehow I can’t see c# team adding this functionality.

As for comments preview – good point. I’ll look into enabling that in a second

Paul Batum says:

I agree that there are alot of ‘missing’ generic constraints (not being able to do where T : enum is particularly annoying!) I can’t say I’ve ever wanted the supertype constraint that you described above – I’m still struggling to imagine a scenario where its useful. Would you be able to fill out the example a bit more?

Sure, one such case would be this:
class MyClass<T>
{
void Add<R>()
{
ICollection<R> collection = _container.GetCollectionOf<R>();
collection.Add(this);
}
}
if you want to be sure that you can always add this to collection you have to enforce R to be base class/inteface of T.

The example I gave in the post, is from WCF.
You have service class (represented by parameter T) that implements one or more interfaces decorated with ServiceContractAttribute.
You use the service class to expose endpoints that are defined by interfaces implemented by that class. So when you call AddServiceEndpoint() and pass a type to it that type must be an interface implemented by service class.
The WCF exposes that functionality by non-generic class ServiceHost. I created a generic wrapper for it, where T is service class, and generic parameters of method AddServiceEndpoint() represent interfaces implemented by that class. Sure, having the constraint I proposed in the post does not shield you from passing any base class of the service class, (including System.Object) instead of interface, but it does shield you from passing any other class/interface, and that is something 🙂

Paul Batum says:

Ahh I see, that WCF explanation helped alot. I guess you had to make the AddServiceEndpoint method do a runtime check on the type instead? Certainly not an ideal solution!