Critical Development

Language design, framework development, UI design, robotics and more.

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.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: