Calling Generic Method with Type Variable

Calling generic method with Type variable

Lets assume that Foo is declared in class Test such as

public class Test
{
public void Foo<T>() { ... }

}

You need to first instantiate the method for type bar using MakeGenericMethod. And then invoke it using reflection.

var mi = typeof(Test).GetMethod("Foo");
var fooRef = mi.MakeGenericMethod(bar);
fooRef.Invoke(new Test(), null);

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.

How to call a method on a generic type variable

Your can defined the methods to be exposed by the generic type into an interface:

    public <T extends MyInterface> void updateStuff(T stuffObject, Calendar startDate, Integer delta) {
stuffObject.setStartDate(startDate);
Calendar calendarEnd = Calendar.getInstance();
calendarEnd = startDate;
calendarEnd.add(Calendar.MONTH, delta);
stuffObject.setEndDate(calendarEnd);
}

interface MyInterface{
void setStartDate(Calendar c);
void setEndDate(Calendar c);
}

invoking correct generic method using Type variable, with out and ref

This can be done through reflection, although finding the correct overload is a bit messy:

class Program
{
static void Main(string[] args)
{
List<string> p2 = new List<string>();
string p3;
string p4 = "input string";
string result = MethodCaller(typeof(DateTime), ref p2, out p3, p4);
}

public static string MethodCaller(Type theType, ref List<string> p2, out string p3, string p4)
{
MethodInfo method = (from m in typeof(Example).GetMethods()
let p = m.GetParameters()
where m.Name == "Method"
&& p.Length == 3
&& p[0].ParameterType.IsByRef
&& p[0].ParameterType.HasElementType
&& p[0].ParameterType.GetElementType() == typeof(List<string>)
&& p[1].ParameterType.IsByRef
&& p[1].ParameterType.HasElementType
&& p[1].ParameterType.GetElementType() == typeof(string)
&& p[2].ParameterType == typeof(string)
select m).Single();
MethodInfo genericMethod = method.MakeGenericMethod(theType);
object[] parameters = new object[] { null, null, p4 };
string returnValue = (string)genericMethod.Invoke(null, parameters);
p2 = (List<string>)parameters[0];
p3 = (string)parameters[1];
return returnValue;
}
}

static class Example
{
public static string Method<T>(ref List<string> p2, out string p3, string p4)
{
p2 = new List<string>();
p2.Add(typeof(T).FullName);
p2.Add(p4);
p3 = "output string";
return "return value";
}

public static string Method<T>(ref List<string> p2, out string p3, int p4)
{
p2 = new List<string>();
p2.Add(typeof(T).FullName);
p2.Add(p4.ToString());
p3 = "output string";
return "return value";
}
}

Call generic method by type

How to convert result to type for call DoSomething method?

You cannot do that statically, because your code does not know the type at compile time, and the object is already of the correct type. One way to do it in .NET 4.0 and later is to use dynamic instead of object for the result, like this:

dynamic result = generic.Invoke(null, new object[] { "just a string" });
result.DoSomething(); // This will compile

You can do it like this only if you are 100% certain that the DoSomething() method is going to be there at runtime. Otherwise, there is going to be an exception at runtime, which you would need to catch and process.

Passing a (local) variable to a generic method as Type

You have to decide whether you'd like to use generics or types, but you can not 'switch' to generics on the fly.
Speaking of this example

var localType = typeof(MyDTOClass);
var result = MyGenericMethod<localType>(string path);

you just don't need first line, instead it should be

 var result = MyGenericMethod<MyDTOClass>(string path);

Speaking of other sample

IEnumerable GetDeserializedJson(string pathToJsonFile, Type dtoType) 
{
IEnumerable result;
using (TextReader file = File.OpenText(pathToJsonFile))
{
result = JsonSerializer.DeserializeFromReader<IEnumerable<dtoType>>(file);
}
return result.OfType<dtoType>.Where(x => x != null);
}

You can declare generic method instead:

IEnumerable<TDto> GetDeserializedJson<TDto>(string pathToJsonFile) where TDto : DtoBaseClass
{
IEnumerable<TDto> result;
using (TextReader file = File.OpenText(pathToJsonFile))
{
result = JsonSerializer.DeserializeFromReader<IEnumerable<TDto>>(file);
}
return result.OfType<TDto>.Where(x => x != null);
}

In this way you don't even have to cast to your type and you have constraint where TDto : DtoBaseClass so compiler will not allow you use classes, not derived from you base dto class.

How to call generic method with a given Type object?

Your code sample won't work, because the generic method expects a type identifier, not a an instance of the Type class. You'll have to use reflection to do it:

public class Example {

public void CallingTest()
{
MethodInfo method = typeof (Example).GetMethod("Test");
MethodInfo genericMethod = method.MakeGenericMethod(typeof (string));
genericMethod.Invoke(this, null);

}

public void Test<T>()
{
Console.WriteLine(typeof (T).Name);
}
}

Do keep in mind that this is very brittle, I'd rather suggest finding another pattern to call your method.

Another hacky solution (maybe someone can make it a bit cleaner) would be to use some expression magic:

public class Example {

public void CallingTest()
{
MethodInfo method = GetMethod<Example>(x => x.Test<object>());
MethodInfo genericMethod = method.MakeGenericMethod(typeof (string));
genericMethod.Invoke(this, null);

}

public static MethodInfo GetMethod<T>(Expression<Action<T>> expr)
{
return ((MethodCallExpression) expr.Body)
.Method
.GetGenericMethodDefinition();
}

public void Test<T>()
{
Console.WriteLine(typeof (T).Name);
}
}

Note passing the 'object' type identifier as a generic type argument in the lambda. Couldn't figure out so quickly how to get around that. Either way, this is compile-time safe I think. It just feels wrong somehow :/



Related Topics



Leave a reply



Submit