Calling a Generic Method with a Dynamic Type

Passing an instance of a dynamic type to a generic method in a generic class

To answer your question:

var type = typeof(abc);
object instanceToModify = new abc();

var typeToCreate = typeof(GenericClass<>).MakeGenericType(type);
var methodToCall = typeToCreate.GetMethod("GenericMethod");

var genericClassInstance = Activator.CreateInstance(typeToCreate);
methodToCall.Invoke(genericClassInstance, new[] { instanceToModify });

DEMO

But:

If your type is only known at runtime your instance must be handled in a variable declared as object or dynamic. In that case you can change your method signature to:

public object GenericMethod(object obj)
{
// modify the object in some (arbitrary) way
IEnumerable<FieldInfo> fields = obj.GetType().GetRuntimeFields();
foreach (var field in fields)
{
if (field.FieldType == typeof(string))
{
field.SetValue(obj, "This field's string value was modified");
}
}

return obj;
}

There's no need for a generic class/method.

How to call a Generic method on a dynamic type in C#

Since your type is dynamic, you won't know the type of the generic parameter until run-time. For this reason, you must also treat the function as dynamic, because you won't know the "type" of the function's generic signature until run-time.

You must use reflection to call a generic function dynamically. See How do I use reflection to call a generic method?

C# dynamically load generic class with dynamic type parameters per strings and strongly typed result

So i think ultimately your problem boils down to the fact that you want to load dynamic types but then treat them as static such that you can invoke methods using instance.MyMethod() syntax. This is not possible (AFAIC) simply because instance.MyMethod() is compiled down to IL and at that point compiler does not know what instance is.

So provided you do not have any way of knowing what is the method you want to invoke, you have to go full dynamic route, i.e. use reflection APIs to invoke the right method.

So you would need to supply not just class name but also method name or have a convention e.g. always invoke method called 'Validate' or something similar.

A simple listing below illustrates my point:

using System;
using System.Reflection;

namespace Shoes {

public interface IA { }
public class A : IA {}

public interface IB { }
public class B : IB {}

public class SomeKindOfModel<T1, T2> {

}

public class ConcreteModel<T1, T2> : SomeKindOfModel<T1, T2> {
public bool Validate() {
return true;
}
}

public class Program
{
public static void Main()
{
// since i don't have your Invoke.GetClassRef, i just did this, works in .net core
var type = Type.GetType($"Shoes.ConcreteModel`2");

var genType = type.MakeGenericType(new[] { typeof(A), typeof(B) });
var instance = Activator.CreateInstance(genType);
var result = genType.GetMethod("Validate", BindingFlags.Instance | BindingFlags.Public)
.Invoke(instance, null); // instead of null you can pass data etc.
Console.WriteLine(result); // prints True
}
}
}

How to use the `dynamic` when specifying generic type arguments in C#?

It is very important to understand the difference between type "arguments" and type "parameters".

Consider this:

class Foo<T> { } // "T" is a type parameter

...

Foo<int> f; // "int" is a type argument

Type parameters declare what types you can pass to this generic type/method. Type parameters are essentially identifiers, not an existing type. When you pass a type to a generic type/method, the type you passed is called the type argument. This is quite similar to the difference between a method parameter and argument.

So the excerpt is trying to say that given a generic type, you can pass the type dynamic to it, and it will be treated as object by the CLR. It doesn't mean that you can do this:

class A<dynamic> {

}

It means that you can do this:

var list = new List<dynamic>();

Or with the types declared in your code:

C<dynamic> c = new C<dynamic> {c = "foo"};

Calling a static method of generic class with a dynamic type c#

You need to use reflection. MakeGenericType allows you to get the Type with specific generic arguments and then you can get and call any method on it as you like.

void Main()
{
Type t = typeof(int);
Type at = typeof(A<>).MakeGenericType(t);
at.GetMethod("B").Invoke(null, new object[]{"test"});
}

public class A<T>
{
public static void B(string s)
{
Console.WriteLine(s+" "+typeof(T).Name);
}
}

As a performance optimization you could use reflection to get a delegate for each type which you then can invoke without further reflection.

Type t = typeof(int);
Type at = typeof(A<>).MakeGenericType(t);
Action<string> action = (Action<string>)Delegate.CreateDelegate(typeof(Action<string>), at.GetMethod("B"));
action("test");

Calling generic methods of objects of type dynamic

The reason list.Count() gives a runtime error when list is a dynamic is that Count is an extension method - Enumerable.Count<T> - and extension methods can only be discovered at compile time. (Somebody correct me if I'm wrong on this - this is my understanding.)

Before getting the direct answer to your question, take a look at this little toy program.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Example
{
class Program
{
static void Main(string[] args)
{
IEnumerable<int> a = Enumerable.Range(1, 3);
IEnumerable b = a;
IEnumerable<double> c = b.SelectTimesPi();
foreach (var x in c)
{
Console.WriteLine(x);
}
Console.ReadLine();
}
}

static class Extensions
{
public static IEnumerable<double> SelectTimesPi(this IEnumerable source)
{
return source
.Cast<dynamic>()
.Select(x => Math.PI * x)
.Cast<double>();
}
}
}

Its output is the following.

3.14159265358979
6.28318530717959
9.42477796076938

The SelectTimesPi extension method takes any IEnumerable<T> where T is only known at runtime, and then manipulates its elements as dynamic values. The source could be IEnumerable<byte>, IEnumerable<short>, IEnumerable<float>, etc.

You could of course do the same thing with a non-extension method, like you asked. I just used that as the example since it looks like your aim is to ultimately create an extension method.

The trick is in the use of Enumerable.Cast<dynamic> on an IEnumerable.

Back to your original question. All you have to do is make your RenderTable method take the list as an IEnumerable instead of a dynamic, then use Enumerable.Cast<dynamic>.

public static MvcHtmlString RenderTable(IEnumerable list, string id, string cssClass)
{
IEnumerable<dynamic> dynamicList = list.Cast<dynamic>();
int x = dynamicList.Count();
...
}

Now the call to Count is not dynamically dispatched, so the extension method will work fine.

In summary:

IEnumerable<int> x = ...;
(x as IEnumerable).Cast<dynamic>().Count(); // does work
(x as dynamic).Count(); // does not work

Make Method Generic from Dynamic Type

You don't need to modify the method at all. What you need to do is take the Type returned and use that to create a generic method using MakeGenericMethod that you can then invoke.

var dynamicType = DynamicType.CreateDynamicType(properties);
var method = mlContext.Model.GetType().GetMethod("CreatePredictionEngine");
System.Type type2 = ...; // however/wherever you get this instance of Type from
var generic = method.MakeGenericMethod(dynamicType, type2);
var param1 = ...; // however/wherever this comes from
var param2 = ...; // however/wherever this comes from

generic.Invoke(mlContext.Model, param1, param2);

That the type is generated dynamically by your CreateDynamicType method is not relevant. You can use any instance of System.Type from any source as the instance on which you call MakeGenericMethod.

Now to actually use your engine, assign it to a dynamic. This allows you to dynamically call the Predict method.

dynamic engine = (dynamic)genericMethod.Invoke(mlContext.Model, args);
var dest = engine.Predict(source)

Alternatively, you're right, you could create another generic method for Predict and invoke that with reflection.

Without knowing the rest of your code, you'll have to continue dynamic calls using dest until you're done with it, unless you can get back to a point where you can then call normal type safe C#.



Related Topics



Leave a reply



Submit