How to Call Generic Method with a Given Type Object

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 :/

Get object type and use in generic method call

This won't work because type arguments are the actual names of types, resolved at compile time by the compiler. It needs the name of a type, but you're giving it the name of a variable instead.

Type entityType = lstItemsModifiedSinceLastSync.FirstOrDefault().GetType();
importResult = importService.Import<entityType>(lstItemsModifiedSinceLastSync,
currentJobQEntry, session, financialsIntegrationContext);

It is possible to call generic methods with type parameters determined at runtime, but you have to use reflection. For your case, something like this (not tested):

//  Get the entity type by reflection too, so we don't have to worry about
// crashing on an empty list.
Type entityType = lstItemsModifiedSinceLastSync.GetType().GenericTypeArguments.First();

MethodInfo method = importService.GetType().GetMethod("Import");
MethodInfo generic = method.MakeGenericMethod(entityType);

generic.Invoke(importService, new object[] {
lstItemsModifiedSinceLastSync,
currentJobQEntry,
session,
financialsIntegrationContext
});

It's up to you whether you consider this more or less ugly than what you've got already. And if Import has overloads, you'll have to rummage through a list of MethodInfo objects to find the right one.

By the way, you could also use type information on lstItemsModifiedSinceLastSync to get its type parameter, instead of relying on there being an item in the list. If there's not, FirstOrDefault() will return null and that line will throw an exception.

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.

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);

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 to call generic property from another generic method

You can add another constraint 'class' to NavigateandReturntheObject

public T NavigateandReturntheObject<T>() where T : class,new()

Complete Code.

public class TestBase
{
public T NavigateandReturntheObject<T>() where T : class,new()
{
//do navigate to page stuff and return the page object

//previously it was - return new T();

//Now i want to do something like this
return PageObjectBase<T>.PageObject;
}
}

Demo Code

public class TestClass
{
public string Name{get;set;}
public TestClass()
{
Name = "Dummy Name";
}
}

var testBase = new TestBase();
var sample = testBase.NavigateandReturntheObject<TestClass>();
Console.WriteLine(sample.Name);

Output

Dummy Name

how to write a generic function that calls a method of an object

You need to put a constraint for classType argument:

class STRING_TYPE {
value(): string {
return "one";
}
}
class NUMBER_TYPE {
name(): number {
return 1;
}
}

type AnyClass<Return> = new (...args: any[]) => Return

const foo = <Klass extends AnyClass<{ name: () => number }>>(classType: Klass) =>
new classType().name();

const result = foo(NUMBER_TYPE)

Playground

foo expects a class constructor with name method. TS is able to infer return type.

UPDATE

class STRING_TYPE {
name(): string {
return "one";
}
}
class NUMBER_TYPE {
name(): number {
return 1;
}
}

type AnyClass<R> = new (...args: any[]) => R

const foo = <
Return extends { name: () => any },
Klass extends AnyClass<Return>,
>(classType: Klass): ReturnType<InstanceType<Klass>['name']> =>
new classType().name()

foo(NUMBER_TYPE) // number
foo(STRING_TYPE) // string

Call Generic method using runtime type and cast return object

The type IS determined at runtime. It's the type of the reference variable that is object, the actual instance is strongly typed.

That's the best that can be done, considering that you're using reflection to dynamically get access to a method that the compiler has no type information for -- it might not even exist in the build environment.

EDIT: If you know something about the type returned by Deserialize, then you can take advantage of delegate variance. For example:

Type tType = Type.GetType(pLoadOut.Type);
MethodInfo method = typeof(ApiSerialiseHelper).GetMethod("Deserialise", new Type[] { typeof(string) });
MethodInfo generic = method.MakeGenericMethod(tType);
Converter<string,ISomething> deser = (Converter<string,ISomething>)Delegate.CreateDelegate(typeof(Converter<string,ISomething>),generic);
ISomething result = deser(pLoadOut.Data);


Related Topics



Leave a reply



Submit