Generic Type Inference & Constructors

Reflecting over types has always been a pretty straight forward exercise. But reflecting over generic types:

 

public class Foo<T> {
  ...
}

and/or types that have generic methods:

public class MathUtils {
  public static T Max<T>(T x, T y) {
    ...
  }
}

adds an extra element of fun to the exercise. For example, given this class definition:

public class MathUtils {
  public static int Max(int x, int y) {
    return (x > y ? x : y);
  }
  public static T Max<T>(T x, T y) where T : IComparable {
    return (x.CompareTo(y) > 0 ? x : y);
  }
}

how would you use reflection to locate only the generic version of MathUtils.Max that takes a single generic type argument (T), has two parameters whose type is the same as the generic type argument, and that requires that type to implement the IComparable interface? For the archives, here's one possible solution:

MethodInfo maxMethod =
  Array.Find(
    typeof(MathUtils).GetMethods(),
    delegate(MethodInfo mi)
    {
      if (mi.IsGenericMethod && (mi.Name == "Max"))
      {
        Type[] genericArgs = mi.GetGenericArguments();
        ParameterInfo[] parameters = mi.GetParameters();

        return(
          (genericArgs.Length == 1) &&
          (parameters.Length == 2) &&
          (parameters[0].ParameterType == parameters[1].ParameterType) &&
          (parameters[0].ParameterType == genericArgs[0]) &&
          typeof(IComparable).IsAssignableFrom(genericArgs[0])
        );
      }

      return (false);
    }
);

Depending on what you're doing, there are any number of criteria that you might use to locate one or all methods that satisfy a certain requirement. The Array.Find and Array.FindAll methods are a pretty good starting place. They both take an generic array and a Predicate delegate that's applied to each entry of the array. Then depending on whether you called Find or FindAll, the first or all matching entries in the array are returned.

 

What I bumped into today was a realization caused by my desire to write a simple helper class that basically facilitated the use of Array.Find and Array.FindAll in the particular area of reflecting over types. Basically - just a set of overloaded Find and FindAll methods that catered to locating methods that matched a given name, a given predicate, or both; and in flavors that assumed a default set of BindingFlags or allowed you to specify the BindingFlags. Here's how my definition of the helper class started out:

public class MethodFinder {
  Type _targetType;

  public MethodFinder(Type targetType) { // Constructor #1
    _targetType = targetType;
  }

  public MethodFinder(object obj) // Constructor #2
    : this(obj.GetType())
  {
  }

  public MethodInfo Find(Predicate<MethodInfo> methodMatcher, BindingFlags bindingFlags) {
    return Array.Find(_targetType.GetMethods(bindingFlags), methodMatcher);
  }

  // Various other Find overloads not shown...

  public MethodInfo[] FindAll(Predicate<MethodInfo> methodMatcher, BindingFlags bindingFlags) {
    return Array.FindAll(_targetType.GetMethods(bindingFlags), methodMatcher);
  }

  // Various other FindAll overloads not shown...
}

There's not a ton of gain by doing it this way in terms of typing reduction. The gain for me was in the set of overloads for MethodFinder.Find and FindAll that provided name matching default implementations, allowed for the use of default or caller-supplied binding flags, and the ability to instantiate a single MethodFinder to operate on a given type and then proceed to perform multiple searches over that type. In fact, what I found interesting as I worked through this little exercise had nothing at all to do with reflection. I just included the code above for its sample code value for anyone googling around for examples of using reflection with generic types or methods :-)

Here's the issue. As defined above, the MethodFinder class provides two constructors: one that takes a Type parameter indicating the type to be operated on, and another that takes an object reference whose type is to be operated on. That's fine as far as they go, and it's the kind of design that's commonly found on the 1.x .NET platform. But if you have in your hands a value, as opposed to an object reference, then you'd want to avoid doing this:

Rectangle rc = GetRectangleFromSomewhere(); // Rectangle is a structure.
MethodFinder finder = new MethodFinder(rc); // Invokes constructor #2.
MethodInfo intersect = finder.Find("Intersect");

Because the above code used constructor #2 of the MethodFinder class, and because Rectangle is a structure, while the argument to constructor #2 is a reference, a boxing operation ensues. At this point, I thought - "No problem - this is a great place for generics!" What I wanted was to just add a 3rd overload for my constructor that would accept a generic type argument:

public class MethodFinder {
  Type _targetType;

  public MethodFinder(Type targetType) { // Constructor #1
    _targetType = targetType;
  }

  public MethodFinder(object obj) // Constructor #2
    : this(obj.GetType())
  {
  }

  public MethodFinder<T>(T obj) // Constructor #3 (illegal)
    : this(obj.GetType())
  {
  }
}

If I did this (so went my thinking), then this line of code:

MethodFinder finder = new MethodFinder(rc);

would no longer incur the overhead of boxing, because the compiler's type inference abilities would cause it to prefer constructor #3, which would work out to the equivalent of the MethodFinder class having provided an overload for its constructor that looks like so:

public class MethodFinder {
  public MethodFinder(Rectangle obj)
    : this(obj.GetType())
  {
  }
}

...which wouldn't incur any boxing overhead. The problem is that C#'s type inference capabilities (xref the C# 2.0 spec, section 20.6.4, Inference of type arguments) only apply to generic methods other than the constructor. So what I tried above was illegal. The only way to get a generic type parameter into the parameter list for a constructor is to define the enclosing type itself as a generic type:

 

public class MethodFinder<T> {
  ...
  public MethodFinder(T obj) // Constructor #3 (legal)
    : this(obj.GetType())
  {
  }
}

Or better yet:

public class MethodFinder<T> {
  ...
  public MethodFinder() // Constructor #3 (legal)
    : this(typeof(T))
  {
  }
}

Either of the above approaches would eliminate the boxing issue with value types like the Rectangle structure used above. The problem with this approach, however, is that I've now turned the entire MethodFinder class into a generic type. So while I've made it possible to use the MethodFinder class efficiently on value types, I've made the MethodFinder class more of a pain to use in non-generic scenarios. This is because any use of MethodFinder now has to "pass" the generic type argument:

SomeClass obj = GetSomeClassInstanceFromSomewhere();
MethodFinder<SomeClass> finder1 = new MethodFinder<SomeClass>(); // Generic finder.
MethodFinder<SomeClass> finder2 = new MethodFinder<SomeClass>(obj); // Finder for instance.
MethodFinder<SomeClass> finder3 = new MethodFinder<SomeClass>(typeof(SomeClass)); // Finder for type.

Because I've made MethodFinder a generic type, and because the C# compiler doesn't infer type for generic type arguments, I've lost the simpler usage model I used to have:

SomeClass obj = GetSomeClassInstanceFromSomewhere();
MethodFinder finder1 = new MethodFinder(obj); // Finder for instance.
MethodFinder finder2 = new MethodFinder(typeof(SomeClass)); // Finder for type.

Argh! That's when it dawned on me that there was a very simple solution to providing a class design for MethodFinder that catered to both the generic & non-generic scenarios. Instead of trying to define a single class that provided all of the constructor forms I was after, I just needed to do a bit of refactoring. A non-generic base class provides the actual functionality, while a generic derived class supports the usage model for the value type scenario:

public class MethodFinder {
  Type _targetType;

  public MethodFinder(Type targetType) { // Constructor #1
    _targetType = targetType;
  }

  public MethodFinder(object obj) // Constructor #2
    : this(obj.GetType())
  {
  }
}

public class MethodFinder<T> : MethodFinder {
  public MethodFinder() // Constructor #3
    : base(typeof(T))
  {
  }
}

With this solution, both the value type and reference type scenarios are supported. And because of the inheritance relationship between MethodFinder and MethodFinder, you can use a MethodFinder anywhere a MethodFinder is expected:

MethodFinder finder;
finder = new MethodFinder<Rectangle>();
finder = new MethodFinder(typeof(SomeClass));

SomeClass obj = GetSomeClassInstanceFromSomewhere();
finder = new MethodFinder(obj);

All that said, I should note for the record that there's also another way to solve the problem. The primary purpose of this post was to discuss the approach of mixing a generic & non-generic types in an inheritance relationship. But the other way to deal with the fact that the C# compiler doesn't support type inference of generic arguments for constructors is to stick with one non-generic class definition that supports non-generic constructors, and just add generic factory methods as needed that takes the place of a generic constructor:

public class MethodFinder {
  Type _targetType;

  public MethodFinder(Type targetType) { // Constructor #1
    _targetType = targetType;
  }

  public MethodFinder(object obj) // Constructor #2
    : this(obj.GetType())
  {
  }

  public static MethodFinder Create<T>( T obj ) { // "Constructor" #3
    return new MethodFinder(typeof(T));
  }
}

This works as well. And some people might find it preferrable to defining two classes. But others probably don't like the idea of a class that supports normal constructor as well as factory-based construction. I'll leave that decision for the reader. At any rate, it's food for thought...


Posted Dec 12 2005, 11:18 AM by mike-woodring

Comments

Mike wrote re: Generic Type Inference &amp; Constructors
on 12-12-2005 12:48 PM
Bug fix: the first Foo class at the very start of this post is missing it's generic paramater. It should be class Foo<T> {...} But a bug in the .Text editor causes <T> syntax to be stripped when a blog entry is read in order to be edited. So I'm not going to bother with the fix. Chances are I'd fix the Foo<T> typo but create lots of other typos along the way. And besides, you get the idea...
Mike wrote re: Generic Type Inference &amp; Constructors
on 12-12-2005 1:25 PM
Okay, so I couldn't resist trying to fix the Foo<T> bug. So if anyone's aggregator happened to d/l the update I initially made using the tool here (http://tinyurl.com/7eynw), you would have seen that the missing <T> was in fact added, but the remainder of the post was underlined and the fonts were screwy :-)

That was caused by the non-xhtml format of the original post, which caused the tool I switched over to to choke.

I *think* it's all touched up now. And this shouldn't happen again, since all future posts will be made with the above tool.
Christopher Steen wrote Link Listing - December 12, 2005
on 12-12-2005 9:09 PM
CLR Profiler and the WebDev Server [Via: scott ]
DHTML IV: Highlighting a table row or column [Via:...
Jason Haley wrote Interesting Finds
on 12-13-2005 6:38 AM
Andy wrote re: Generic Type Inference &amp; Constructors
on 12-13-2005 8:51 AM
I could be missing something, but wouldn't it also be possible to add a constructor to MethodFinder that takes a ValueType?

public class MethodFinder
{
public MethodFinder(ValueType value)
: this(value.GetType())
{

}
}

I don't think that this would cause any boxing (since the ValueType is never copied to an object), but I could be wrong.
Mike wrote re: Generic Type Inference &amp; Constructors
on 12-13-2005 9:30 AM
Actually, you're right in the case of the MethodFinder I was building. I was caught up in trying to figure out why I couldn't define a constructor that accepted a generic type argument the same as you can for a normal method; and so couldn't see the forest for the trees :-)

But since in my particular case, my MethodFinder only wants to call GetType, then you're absolutely right; and your approach is much simpler.

In the more general API design scenario, where you're trying to preserve as much information about the actual type of a parameter, either of the 2 approaches shown here might come in handy (generic derivative of a non-generic class, or a generic factory method to serve as a generic constructor method).
Ron MacNeil wrote re: Generic Type Inference &amp; Constructors
on 01-31-2006 7:39 PM
Thanks for the generic factory method suggestion - just what I was looking for!

Best,
Ron
p90x wrote p90x
on 10-04-2008 11:02 AM

Quick Question: Regarding the end date of the 24th, will we need to post our final weight on Christmas day? I should be in town, but I’ m just worried I may forget to get on and post it on the 25th. May help everyone what with all the excitement and different

Add a Comment

(required)  
(optional)
(required)  
Remember Me?