Generic Method Executed with a Runtime Type

Generic Method Executed with a runtime type

You can, but it involves reflection, but you can do it.

typeof(ClassExample)
.GetMethod("DoSomething")
.MakeGenericMethod(p.DisplayType)
.Invoke(this, new object[] { p.Name, p.Value });

This will look at the top of the containing class, get the method info, create a generic method with the appropriate type, then you can call Invoke on it.

How do I use reflection to call a generic method?

You need to use reflection to get the method to start with, then "construct" it by supplying type arguments with MakeGenericMethod:

MethodInfo method = typeof(Sample).GetMethod(nameof(Sample.GenericMethod));
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

For a static method, pass null as the first argument to Invoke. That's nothing to do with generic methods - it's just normal reflection.

As noted, a lot of this is simpler as of C# 4 using dynamic - if you can use type inference, of course. It doesn't help in cases where type inference isn't available, such as the exact example in the question.

How do you call a generic method if you only know the type parameter at runtime?

You need to call it using reflection:

var method = typeof(SomeClass).GetMethod("SomeMethod");
method.MakeGenericMethod(someType).Invoke(...);

Calling generic method with a type argument known only at execution time

EDIT: Okay, time for a short but complete program. The basic answer is as before:

  • Find the "open" generic method with Type.GetMethod
  • Make it generic using MakeGenericMethod
  • Invoke it with Invoke

Here's some sample code. Note that I changed the query expression to dot notation - there's no point in using a query expression when you've basically just got a where clause.

using System;
using System.Linq;
using System.Reflection;

namespace Interfaces
{
interface IFoo {}
interface IBar {}
interface IBaz {}
}

public class Test
{
public static void CallMe<T>()
{
Console.WriteLine("typeof(T): {0}", typeof(T));
}

static void Main()
{
MethodInfo method = typeof(Test).GetMethod("CallMe");

var types = typeof(Test).Assembly.GetTypes()
.Where(t => t.Namespace == "Interfaces");

foreach (Type type in types)
{
MethodInfo genericMethod = method.MakeGenericMethod(type);
genericMethod.Invoke(null, null); // No target, no arguments
}
}
}

Original answer

Let's leave aside the obvious problems of calling a variable "interface" to start with.

You have to call it by reflection. The point of generics is to put more type checking at compile time. You don't know what the type is at compile-time - therefore you've got to use generics.

Get the generic method, and call MakeGenericMethod on it, then invoke it.

Is your interface type itself actually generic? I ask because you're calling MakeGenericType on it, but not passing in any type arguments... Are you trying to call

Method<MyNamespace.Interface<string>>(); // (Or whatever instead of string)

or

Method<MyNamespace.Interface>();

If it's the latter, you only need a call to MakeGenericMethod - not MakeGenericType.

Reflection To Execute Class With Generic Type

@rickvbosch 's answer was (very nearly) what I needed and definitely the right way to go. However, since I was trying to do two things effectively, I've upvoted his answer and added what I did to get things working as the accepted answer to my original problem, using his suggestion.

        var instance = Activator.CreateInstance(implementation);
var results = this.GetType()
.GetMethod("CheckForMessages", BindingFlags.NonPublic | BindingFlags.Instance)
.MakeGenericMethod(interfaceUsesType)
.Invoke(this, null) as object[];

if(results.Count() > 0)
instance.GetType()
.GetMethod("DoThis")
.Invoke(instance, new object[] {results});

typeof generic and casted type

typeof returns the static (compile-time) type of the generic parameter T.

GetType returns the dynamic (run-time) type of the value contained in variable item.


The difference is easier to see if you make your method non-generic. Let's assume that B is a subtype of A:

public void NonGenericMethod(A item)
{
var typeOf = typeof(A);
var getType = item.GetType();
}

In that case, calling NonGenericMethod(new B()) would yield

A
B

Recommended further reading:

  • Run-time type vs compile-time type in C#

Now, you might ask: Why did you use NonGenericMethod(A item) in your example instead of NonGenericMethod(B item)? That's a very good question! Consider the following (non-generic) example code:

public static void NonGenericMethod(A item)
{
Console.WriteLine("Method A");
var typeOf = typeof(A);
var getType = item.GetType();
}
public static void NonGenericMethod(B item)
{
Console.WriteLine("Method B");
var typeOf = typeof(B);
var getType = item.GetType();
}

What do you get when you call NonGenericMethod((A) new B()) (which is analogous to the argument (object) 1 in your example)?

Method A
A
B

Why? Because overload resolution is done at compile-time, not at run-time. At compile-time, the type of the expression (A) new B() is A, just like the compile-time type of (object) 1 is object.

Recommended further reading:

  • When is the generic type resolved in c#?

Use Type as Generic Method Parameter

Ok, as it turns out it is actually fairly simple.
Here's how I did it:

var mappingType1 = mappings[platform.Type].Item1;
var mappingType2 = mappings[platform.Type].Item2;
var method = typeof(Class).GetMethod(nameof(GenericMethod), BindingFlags.NonPublic | BindingFlags.Static);
var genericMethod = method.MakeGenericMethod(mappingType1, mappingType2);
genericMethod.Invoke(null, null);

Strange generic method call with explicit generic type behaviour

From what you are saying, including the comment:

If I try to access any members during runtime it of course throws an exception about non existing members.

it sounds like either a compiler bug or JIT bug. That means to diagnose it we need more knowledge of the compiler you are using (exact version) and/or the JIT you are using. In the case of a compiler bug, it may suffice if you can simply show the IL that has been generated, if you're familiar with that.

Note that the most recent VS preview includes an entirely new JIT (RyuJIT) and enables it by default system-wide, so if you have installed the VS preview, that would be my guess. If so, you can disable it trivially to check.


Note the other option here is something like a using alias that aliases Record to be System.String, or aliases var to be System.String (plus some implicit conversion operators, etc). These are unlikely, but I've seen it happen ;p

Edit: the above is ruled out by your comment:

doing typeof(Record).FullName correctly returns my full type name including namespaces as it should.

So we are left with compiler bug or JIT bug.


If this is a RyuJIT bug, here are the options to disable it (I suggest using the config one first, as it is the easiest to do):

  1. As an environment variable: set COMPLUS_useLegacyJit=1

  2. In the registry: Set HKLM or HKCU, Software\Microsoft.NETFramework. Key name: useLegacyJit. Type: REG_DWORD. Value: 1

  3. In an app.exe.config file:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
    <runtime>
    <useLegacyJit enabled="1" />
    </runtime>
    </configuration>

(these come from a separate discussion with MS that I had previously)



Related Topics



Leave a reply



Submit