In this article, we are going to learn how to call a generic method in C# using reflection.

To download the source code for this article, you can visit our GitHub repository.

Generic methods are an all-time favorite of library developers for their inherent reusability and type inference benefits. But because of the need for explicit type arguments in most cases (except when type inference occurs implicitly), we can’t always make the best use of them in compile time. This is where reflection comes into play. 

Let’s dive in.

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

Call Generic Method Directly

To begin with, let’s assume we have a library that features some caption generation routines: 

public class CaptionBuilder
{
    public string? ClassCaption<T>()
    {
        var type = typeof(T);

        return type.GetCustomAttribute<DisplayNameAttribute>()?.DisplayName ?? type.Name;
    }
    // omitted other routines for the brevity
}

The library ships with a CaptionBuilder class containing a few generic routines. For now, let’s just talk about the ClassCaption method. This generates a display name from a type argument coming as part of a generic <T> type-placeholder.

We also have two record classes:

[DisplayName("Goods Store")]
public record class GoodsStore(string? Name, string? Area);

[DisplayName("Employee")]
public record class Stuff(string? Name, string? Designation);

We are going to use them for our caption generation example:

var builder = new CaptionBuilder();

var caption1 = builder.ClassCaption<GoodsStore>();
var caption2 = builder.ClassCaption<Stuff>();

Assert.Equal("Goods Store", caption1);
Assert.Equal("Employee", caption2);

As we see, with the help of a CaptionBuilder instance, we get the captions for GoodsStore and Stuff types. For this to work, we have to call the ClassCaption method with the desired type in the <T> placeholder. The output rightly reflects the display names of the target classes.

Now, let’s see what happens if we try with a type variable that receives a calculated value from elsewhere:

Type ResolveType(string shortCode)
{
    return shortCode switch
    {
        "GDS" => typeof(GoodsStore),
        "EMP" => typeof(Stuff),
        _ => throw new NotImplementedException(),
    };
}

var type = ResolveType("GDS");

// This line does not compile
// var directResult = new CaptionBuilder().ClassCaption<type>();

Obviously, we can’t pass a variable in the <T> placeholder and hence can’t call the ClassCaption method!

We can solve this by implementing an overload of ClassCaption that takes a type parameter instead of a generic type argument and reuses the existing code block of ClassCaption. But that too is not feasible for an external library.

Call Generic Method Using Reflection

 Alternatively, we can implement a non-generic wrapper of the CaptionBuilder routines with the help of reflection:

public class NonGenericCaptionBuilder
{
    private readonly CaptionBuilder _captionBuilder = new();

    public string? ClassCaption(Type type)
    {
        var baseMethod = typeof(CaptionBuilder)
            .GetMethod(nameof(CaptionBuilder.ClassCaption))!;

        var genericMethod = baseMethod.MakeGenericMethod(type)!;

        return (string?)genericMethod.Invoke(_captionBuilder, Array.Empty<object>());
    }
}

So, we come up with a NonGenericCaptionBuilder class. Inside it, we start with a readonly instance of CaptionBuilder. As part of our non-generic routines, we are going to delegate all invocations to this _captionBuilder instance.

Right next to it, we implement the non-generic ClassCaption method. Inside this routine, we first extract the MethodInfo of the original ClassCaption method. Then, we construct a generic version of it by calling the MakeGenericMethod() with the target type. To invoke this generic method, we need two things: an instance of the owner class of the method and the necessary arguments for the parameters of the original method in form of an object array. As we’re dealing with a parameter-less method, in our case, _captionBuilder and an empty array does the job.

If you want to learn more about reflection, we strongly suggest reading our Reflection in C# article, where we cover this feature in great detail.

Now, we’re able to generate a caption from a type variable:

var type = ResolveType("GDS");

// The line below does not compile
// var directResult = new CaptionBuilder().ClassCaption<type>();

var reflectedResult = new NonGenericCaptionBuilder().ClassCaption(type);

Assert.Equal("Goods Store", reflectedResult);

Static Method

CaptionBuilder class also has a static generic method:

public static string? StaticCaption<T>() => typeof(T).Name.ToUpper();

If we want a non-generic version of this, we need a slight change from the previous one:

public static string? StaticCaption(Type type)
{
    var baseMethod = typeof(CaptionBuilder)
        .GetMethod(nameof(CaptionBuilder.StaticCaption))!;

    var genericMethod = baseMethod.MakeGenericMethod(type)!;

    return (string?)genericMethod.Invoke(null, Array.Empty<object>())!;
}

During the invocation at the end, we need to pass null instead of the _captionBuilder instance. This is because a static method does not belong to a specific instance.

As we try this non-generic StaticCaption method:

var type = ResolveType("GDS");

var reflectedResult = NonGenericCaptionBuilder.StaticCaption(type);

Assert.Equal("GOODSSTORE", reflectedResult);

We get the result we expect.

Multiple Generic Type Arguments

No matter how many generic types a method signature has:

public string? ParentChildCaption<TParent, TChild>()
{
    var caption1 = ClassCaption<TParent>();
    var caption2 = ClassCaption<TChild>();

    return $"{caption2} of {caption1}";
}

We can manage its non-generic invocation:

public string? ParentChildCaption(Type parentType, Type childType)
{
    var baseMethod = typeof(CaptionBuilder)
        .GetMethod(nameof(CaptionBuilder.ParentChildCaption))!;

    var genericMethod = baseMethod.MakeGenericMethod(parentType, childType)!;

    return (string?)genericMethod.Invoke(_captionBuilder, Array.Empty<object>());
}

All we need is to call the MakeGenericMethod() with the necessary types. And of course, we have to pass them in the appropriate order:

var type1 = ResolveType("GDS");
var type2 = ResolveType("EMP");

var reflectedResult = new NonGenericCaptionBuilder().ParentChildCaption(type1, type2);

Assert.Equal("Employee of Goods Store", reflectedResult);

We can see that our non-generic version rightly produces the expected outcome.

Call Generic Method With Parameters

We may also have a generic method with generic parameters:

public string? ObjectCaption<T>(T obj)
{
    if (obj is null) return null;

    if (obj.GetType().GetProperty("Name") is { } nameProperty)
        return nameProperty.GetValue(obj)?.ToString();

    return obj?.ToString();
}

So, we aim for a non-generic version that can be called with dynamic object arguments:

public string? ObjectCaption(object obj)
{
    if (obj is null) return null;

    var baseMethod = typeof(CaptionBuilder)
        .GetMethod(nameof(CaptionBuilder.ObjectCaption))!;

    var genericMethod = baseMethod.MakeGenericMethod(obj.GetType())!;

    return (string?)genericMethod.Invoke(_captionBuilder, new object[] { obj })!;
}

This is no different than the previous ones except that we have to supply the arguments in the final invocation part.

When we try this method on a GoodsStore object:

var store = new GoodsStore("Apex", "Street 12");

var directResult = new CaptionBuilder().ObjectCaption(store);

var reflectedResult = new NonGenericCaptionBuilder().ObjectCaption(store);

Assert.Equal("Apex", directResult);
Assert.Equal(directResult, reflectedResult);

We get the same result as we would with the generic version.

Overloaded Methods

Now, let’s think about overloads of a generic method:

public string? ComboCaption<T1, T2>(T1 item1, T2 item2) 
{
    var caption1 = ObjectCaption(item1);
    var caption2 = ObjectCaption(item2);

    return $"{caption1} ({caption2})";
}

public string? ComboCaption<T1, T2>(T1 item1, T2 item2, bool capitalize)
{
    var caption = ComboCaption(item1, item2);

    return capitalize ? caption?.ToUpper() : caption;
}

Here, we have two versions of ComboCaption method with a different number of parameters.

If we want to invoke the one with three parameters only, we can do that too:

public string? ComboCaption(object item1, object item2, bool capitalize)
{
    var baseMethod = typeof(CaptionBuilder)
        .GetMethods()
        .Single(m =>
            m.Name == nameof(CaptionBuilder.ComboCaption)
            && m.GetParameters().Length == 3);

    var genericMethod = baseMethod.MakeGenericMethod(item1.GetType(), item2.GetType())!;

    return (string?)genericMethod.Invoke(_captionBuilder, new object[] { item1, item2, capitalize })!;
}

The difference we see here is actually not related to the generic types but the resolution of the specific overload. This resolution part varies depending on the method signature we want to deal with. In our case, we just look for a “ComboCaption” method with three parameters. The rest of the method is pretty similar to the previous ones.

Calling of this non-generic version with sample objects:

var store = new GoodsStore("Apex", "Street 12");
var stuff = new Stuff("John", "Manager");

var directResult = new CaptionBuilder().ComboCaption(stuff, store, true);

var reflectedResult = new NonGenericCaptionBuilder().ComboCaption(stuff, store, true);

Assert.Equal("JOHN (APEX)", directResult);
Assert.Equal(directResult, reflectedResult);

Eventually invokes the second generic ComboCaption method and produces the desired output.

Caveats of Reflecting Generic Method

Like other reflection features, dealing with a generic method is also prone to hidden breakages and runtime errors.

One such common problem occurs due to the generic type constraint:

public string? RestrictedCaption<T>() 
    where T : IFormattable
{
    return typeof(T).Name;
}

The IFormattable restriction, in compile-time, prevents us from calling this routine with an unsupported type.

Sadly, we can not implement this constraint in a non-generic version:

public string? RestrictedCaption(Type type)
{
    var baseMethod = typeof(CaptionBuilder)
        .GetMethod(nameof(CaptionBuilder.RestrictedCaption))!;

    var genericMethod = baseMethod.MakeGenericMethod(type)!;

    return (string?)genericMethod.Invoke(_captionBuilder, Array.Empty<object>());
}

As we see, no constraint here to prevent it from calling with non-supported types.

As a result, if we unknowingly apply it on GoodsStore which does not support IFormattable:

var type = typeof(GoodsStore);

Assert.ThrowsAny<ArgumentException>(() => new NonGenericCaptionBuilder().RestrictedCaption(type));

We fail for an exception.

What we can do at best is to throw a friendly exception to convey the proper failure message:

public string? ImprovedRestrictedCaption(Type type)
{
    if (!typeof(IFormattable).IsAssignableFrom(type))
        throw new NotSupportedException($"{type.Name} does not implement IFormattable interface");

    var baseMethod = typeof(CaptionBuilder)
        .GetMethod(nameof(CaptionBuilder.RestrictedCaption))!;

    var genericMethod = baseMethod.MakeGenericMethod(type)!;

    return (string?)genericMethod.Invoke(_captionBuilder, Array.Empty<object>());
}

Despite these risks, dynamic invocation of a generic method is surely a strong feature of reflection.

Conclusion

In this post, we have learned how to call a generic method using reflection. We also come to know the risk associated with such invocations. 

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!