Not enough information to be generic…

This problem arose in a library I was writing last week, there were a number of ways to resolve it, all of them involving some amount of pain / ugliness in the code. The only question was where that pain would be.

The problem

We want to call a generic method with the exact runtime type of the current class.

public class FooFactory
{
	public Foo GetFoo<T>(int id) 
		where T : FlagBase
	{
		// ...
	}
}
public abstract class FlagBase
{
	private void Frobnicate()
	{
		var foo = fooFactory.GetFoo<???>(42);
		// ...
	}
}

So, our goal is to get the runtime type of the class in place of the ???.

The solution

It’s relatively simple to imagine ways we can get the deriving class to provide this information.

Since we’re using inheritance, an abstract method fits the bill:

public abstract class FlagBase
{
	protected abstract Foo GetFoo(FooFactory fooFactory, int id);
}

But now we must override this method in each and every deriving class, must get the type parameter correct each time, and must assume the deriving class is actually calling fooFactory correctly.

Another alternative could be to go for a CRTP pattern:

public abstract class FlagBase {}

public abstract class FlagBase<T> : FlagBase
	where T : FlagBase<T>
{
	private void Frobnicate()
	{
		var foo = fooFactory.GetFoo<T>(42);
		// ...
	}
}

This keeps the deriving classes clean but there’s a huge caveat, and one that we can’t enforce. If we create a derived class FlagA : FlagBase<FlagA> we can then create FlagB : FlagA. Unfortunately, we now have FlagB : FlagBase<FlagA> and the type parameter is not the actual runtime type of the class! To make this work we would have to restrict the inheritance from FlagBase<T> to be only one level deep, something that we cannot do.

There are other patterns that we can attempt to apply, but none of them allow us to keep the consuming code out of the picture. In this scenario at least, requiring the caller to provide this information is a bad idea:

  • It’s remarkably easy to provide the wrong information.
  • Every extra requirement you make of a consumer is another opportunity for a mistake.
  • It simply makes the library harder to consume.

Sometimes forcing the consumer to do the extra work is just something we have to live with for the type system we’re in, but here there’s a cleaner alternative – for the consumer at least – and a way that we can ‘internalise’ the pain.

What’s left in the toolbag?

this.GetType() and reflection to the rescue!
Not a sentence you often want to hear…

Searching Google or StackOverflow for anything reflection-based invariably leads to suggestions of invoking the required object by name and, particularly if you’re not very familiar with C#, it can be easy to be led down the garden path. In my opinion (and experience) hard coded object names scream of refactoring pain in the future. While any mistake should be picked up in your test suite, you want to strive to make refactoring as easy as possible, and why have a strongly typed language and a compiler if you’re not going to make them do at least some of the leg work?

For what we’re trying to achieve here, I prefer to get hold of a ‘strongly typed’ method group then change the generic type of that reference. It avoids strings and if applied carefully can be done safely.

public abstract class FlagBase
{
	private void Frobnicate()
	{
		var runtimeType = this.GetType();
		Func<int, Foo> methodGroup = fooFactory.GetFoo<FlagBase>;
		var methodInfo = methodGroup.Method
			.GetGenericMethodDefinition()
			.MakeGenericMethod(runtimeType);
		
		//var foo = fooFactory.GetFoo<???>(42);
		// ...
	}
}

The main type safety (yes, I’m using that term fast-and-loose) mistake we can make here is supplying the type parameters incorrectly, either the wrong number or types that don’t satisfy any type constraints. Fortunately, having the reference to the method right next to us in code helps identify the former at a glance. Satisfying T : FlagBase is guaranteed here, as we’re doing this work inside FlagBase and the runtime type of the object clearly satisfies the constraint.

Now we’re one step further along: we have a reference to the correctly-generically-typed method we want to call. But how do we actually go about calling it?

Using what we’ve built

We could simply call invoke on the MethodInfo object.

var foo = (Foo)methodInfo.Invoke(fooFactory, new object[] { 42 });

This leads to questions of type safety of the method’s parameters and any return value, as well as concerns about performance. Also, there’s only so much safety that I’m willing to throw away to make a library easier to consume.

The answer lies in System.Linq.Expressions for help in compiling this reference to something safely callable. Unfortunately for us, that has its own pitfalls to overcome…