Minimizing Reflection Usage with Generics and Dynamic

September 15th 2014 Reflection .NET Framework

It's always best to completely avoid using reflection, but unfortunately that's not always possible. Sometimes you need to use APIs, which are not strongly typed. In such cases you should transition from reflection to strongly typed code as soon as possible: because of performance, and because of code readability as well. In this blog post I'll describe a couple of techniques which are useful in situations like this.

ASP.NET MVC binders are a good example of an API forcing you into reflection, because it provides you with an instance of Type class instead with a generic type. In binders you often need to interact with a DAL (data access layer), therefore I decided to use a simplified DAL API for demonstration:

// repository class for retrieving data
public class Repository
{
    public IQueryable<TEntity> GetItems<TEntity>() where TEntity : class
    {
        // empty result set instead of actual data
        return Enumerable.Empty<TEntity>().AsQueryable();
    }
}

// interface implemented by entities for exposing the key
public interface IIdentifiable<TKey>
{
    TKey Id { get; }
}

// sample entity
public class Entity : IIdentifiable<int>
{
    public int Id { get; set; }
}

Such a DAL can be used very easily when you're using strongly typed code:

var entity = repository.GetItems<Entity>().SingleOrDefault(e => e.Id == key);

Of course, to do that, you need to know the type of entity, you're trying to retrieve. If instead of having the actual type you only have an instance of the Type class, representing it, it suddenly gets much more complicated. There's no other way, than to use reflection:

var method = repository.GetType().GetMethod("GetItems").Dump();
var genericMethod = method.MakeGenericMethod(entityType);
var queryable = genericMethod.Invoke(repository, new object[0]);

The code is certainly not nice, but it wasn't that difficult to do it, either. But we're only halfway there. The next step requires using LINQ. Since we don't know the type of key in advance either, we would need to create the lambda expression using reflection, as well. We don't really want to do that, so we can take a different approach instead: we can write a method wrapping all our logic and use reflection only for calling it:

public TEntity GetEntityById<TEntity, TKey>(Repository repository, TKey key)
    where TEntity : class, IIdentifiable<TKey>
{
    return repository.GetItems<TEntity>()
        .SingleOrDefault(e => EqualityComparer<TKey>.Default.Equals(e.Id, key));
}

var method = GetType().GetMethod("GetEntityById").Dump();
var genericMethod = method.MakeGenericMethod(entityType, key.GetType());
var entity = genericMethod.Invoke(this, new object[] { repository, key });

This approach allows us to minimize the amount of reflection code, we need to write, and simplifies it at the same time (we can design the method to have as simple signature as possible).

There is a way to avoid even this minimal block of reflection code, by taking advantage of generic arguments inference. We need a parameter matching the generic argument of the method, to make it work:

public TEntity GetByEntityId<TEntity, TKey>(TEntity dummy,
    Repository repository, TKey key)
    where TEntity : class, IIdentifiable<TKey>
{
    return GetEntityById<TEntity, TKey>(repository, key);
}

Based on dummy parameter's TEntity type, the compiler will now be able to infer TEntity and TKey types. As a side effect we need to create an instance of the type which shouldn't be a problem as long as it has a default parameter-less constructor:

dynamic dummyEntity = Activator.CreateInstance(entityType);
var entity = GetEntityById(dummyEntity, repository, key);

Notice that I needed to use dynamic for my dummy instance. I can't cast it to the correct type, so without dynamic the compiler would assume it is of type object and the code wouldn't even compile. By using dynamic you postpone the type checking until runtime, when the actual type is already known.

I haven't done any measurements to compare the performance of both approaches: reflection and dynamic with dummy object instantiation. You shouldn't ever need to use such code in tight loops or other performance sensitive scenarios, anyway, so feel free to use the one, which you like better.

Get notified when a new blog post is published (usually every Friday):

If you're looking for online one-on-one mentorship on a related topic, you can find me on Codementor.
If you need a team of experienced software engineers to help you with a project, contact us at Razum.
Copyright
Creative Commons License