Multiple Generic Type Parameters & Limitations on Constraints
Posted by Dan Vanderboom on July 23, 2008
After daily use for years, I keep running into new situations with newer C# language features, even with generics which have been around since .NET 2.0. Just as a quick refresher, in case you’re not familiar with generics or have been stuck on .NET 1.1 for a while, here’s a simple generic method to succinctly determine if some type is decorated with an attribute:
public static bool HasAttribute<T>(this Type Type) { foreach (Attribute attr in Type.GetCustomAttributes(typeof(T), true)) { if (attr.GetType() == typeof(T)) { return true; } } return false; }
I can call it like this:
bool HasParseAttr = SomeObject.GetType().HasAttribute<ParseAttribute>();
Generic constraints are defined with a “where” clause, so you can ensure that a type that’s passed in implements a specific interface, you can ensure the type is a reference type (class) or a value type (struct), you can ensure that it has a constructor (new), or you can ensure it inherits from some other type.
public static bool HasAttribute<T>(this Type Type) where T : Attribute { // ... }
Here I’m ensuring that T inherits from the Attribute class.
Some of the code I wrote today required different constraints on multiple type parameters. I tried using &&, and commas, but those didn’t work, so a quick Google search gave me the answer—the use of multiple where clauses:
private void RegisterDataBindingMethods<AttributeType, DelegateType>(ref Dictionary<string, DelegateType> Methods) where AttributeType : Attribute where DelegateType : Delegate { // ... }
The problem is that “special types”, such as object and Delegate, can’t be used for generic constraints. For object, you can use class or new(), but why not Delegate? Is this an artificial restriction?
Within the body of this method, I ran into another odd problem:
Delegate del = Delegate.CreateDelegate(typeof(DelegateType), this, mi); Methods.Add(PropertyName, (DelegateType)del);
This won’t compile, because there’s no way to know if del can be casted to a DelegateType at compile time. However, if I add the constraint where DelegateType : class, then I can do this:
Methods.Add(PropertyName, (del as DelegateType));
This works fine, because it will attempt at runtime to convert it. If it fails, the resulting expression will be null. Although C# isn’t always very intuitive, there is almost always a way to accomplish what you’re attempting.
I’ve been impressed by the ways in which fundamental design patterns, as well as my general development approach, have changed (sometimes drastically) due to a handful of new language features, and while most of that has been due to the fantastic features added in C# 3.0, I occasionally run into situations where older features like generics still surprise me.
Leave a Reply