I want to create dynamic assembly with generic class:
class TestClass<T> where T : new() {
public T TestMethod() {
return f();
}
private Func<T> f;
}
So, I created class, added generic argument, set constraints and created delegate like this:
var fieldType = typeof(Func<>).MakeGenericType(TArg);
// TArg = testClassBuilder.DefineGenericParameters("T")[0];
Then using IL generator I tried to emit calling Invoke method:
ilGenerator.Emit(OpCodes.Callvirt, fieldType.GetMethod("Invoke"));
But I get NotSupportedException on GetMethod("Invoke") call. So, how to call this delegate using Emit?
You cannot call GetMethod on typeof(Func<>).MakeGenericType(TArg), because in this instance, TArg is a GenericTypeParameterBuilder, and the Type object returned by MakeGenericType doesn't know how to get the relevant methods.
Instead use TypeBuilder.GetMethod like this:
ilGenerator.Emit(
OpCodes.Callvirt,
TypeBuilder.GetMethod(
typeof(Func<>).MakeGenericType(genParam),
typeof(Func<>).GetMethod("Invoke")
));
Related
I want to invoke a generic method having two different generic type parameters. Below is what my method looks like:
public class ABC<T> : IABC<T> where T: class,new()
{
public T Merge<T1>(T child, T parent, T1 rule){
}
}
I want to invoke the Merge method from another method. Below is what I have tried to invoke it.
Type mergerType = GetType().GetGenericTypeDefinition();
Type constructed = mergerType.MakeGenericType(new[]
{
childValue.GetType()
});
object mergerInstance = Activator.CreateInstance(constructed);
MethodInfo methodInfo = GetType().GetMethod("Merge");
MethodInfo method = methodInfo.MakeGenericMethod(ruleValue.GetType());
mergedObject = method.Invoke(mergerInstance, new[]
{
childValue,
parentValue,
ruleValue
});
While doing this I am getting a exception "object does not match target type" after method.invoke().
I cannot change the class or method definition of ABC class or Merge method because many other classes are implementing the IABC interface.
So can someone answer how can I invoke the Merge method.
You have to use the Merge method of the newly constructed type.
Change MethodInfo methodInfo = GetType().GetMethod("Merge");
to: MethodInfo methodInfo = constructed.GetMethod("Merge");
I need to call a generic method that takes a generic Func as one of its parameters, where the Type parameter is known only at runtime. This part of the code is an object mapper, which maps properties between a source and a target object. ViewModelBase is the root of classes that are considered "target" objects.
The method that I want to call (defined on ObjectMapperBuilder) has this signature:
public static ObjectMapperBuilder<TTarget> Create(
Type sourceType,
MappingDirection direction,
Func<TTarget, IDictionary<String, object>> getDictionaryFromTarget = null
);
In my base class, I want to call the above method, but use the most derived type as my type parameter:
public ViewModelBase {
private ConcurrentDictionary<string, object> _propertyValues;
public ViewModelBase (object sourceObject) {
Type tTarget = this.GetType();
// 1. How do I create the Func? All it does is return a private member.
// This is wrong because it uses a compile-time generic parameter.
Func<TTarget,IDictionary<String,object>> myFunc = (vm) => vm._propertyValues;
// 2. Ho do I call the Create method using reflection to specify the
// TTarget generic parameter at runtime?
var myMapper = ObjectMapperBuilder<TTarget>.Create(
sourceObject.GetType(),
MappingDirection.Bidirectional,
myFunc
);
// Do stuff with myMapper.
...
}
The purpose of this exercise is to be able to create the mapper in a method on the base class. The mapper must be created using the most derived type because I cache mappers according to source and target types, and different derived types need different mappers.
This might be a job for Expression trees and Activator, but I can't figure it out.
Part of the answer might be found in the answer to this question:
Runtime creation of generic Func<T>
This might be a simple answer, but could you make your view model base type generic, e.g.:
public class ViewModelBase<T> where T : ViewModelBase<T>
Allowing you to apply the inheritance:
public class SubViewModelBase: ViewModelBase<SubViewModelBase>
That way, your implementation would simply be:
Func<T, IDictionary<string, object>> props = (vm) => vm._propertyValues;
var mapper = ObjectMapperBuilder<T>.Create(
sourceObject.GetType(),
MappingDirection.Bidirectional,
props);
I settled on a solution with a compromise. I created a method "GetProperties" which does what I want, then wrap it in a delegate using Delegate.CreateDelegate.
protected static IDictionary<string, object> GetProperties(ViewModelBase viewModel)
{
return viewModel._propertyValues;
}
protected Delegate GetPropertiesFunc()
{
Type funcType = typeof(Func<,>).MakeGenericType(this.GetType(), typeof(IDictionary<String,object>));
MethodInfo method = typeof(ViewModelBase).GetMethod("GetProperties",
BindingFlags.NonPublic | BindingFlags.Static
);
return Delegate.CreateDelegate(funcType, method);
}
When I later need the Delegate as a particular Func, I call GetPropertiesFunc and pass it to Activator.CreateInstance, which works successfully.
I'm trying to register a generic that derives from a base class in the following way, but getting the error :
cannot convert MyCallback<T> expression to type MyCallback<Event>
I was hoping the constraints would make this possible but am I missing something?
public class Event
{ };
public delegate void MyCallback<T>(T arg1) where T : Event;
static class EventDispatcher
{
public static Dictionary<string, MyCallback<Event>> eventTable = new Dictionary<string, MyCallback<Event>>();
static void RegisterCallback<T>(MyCallback<T> callback) where T : Event
{
eventTable.Add("test", callback);
}
}
When you have a MyCallback<Event> you're saying that you have a method that can take any type of event. It can accept an EventOne, or an EventTwo, or a SomeOtherEvent.
Let's say I call RegisterCallback and pass in a delegate pointing to a method with this signature:
public static void Foo(SomeOtherEvent arg)
If your code would work, and I could assign that to a MyCallback<Event>, then I could pass in an EventOne instance to that method when calling it. That's obviously a problem.
There's a term for that; you're expecting MyCallback to be covariant with respect to it's generic argument. In fact, it's contravariant. If I have a method that can accept any type of event, I can clearly pass in a SomeEvent, or a SomeOtherEvent, meaning I could assign a MyCallback<Event> to a MyCallback<SomeOtherEvent>, rather than the other way around.
If you want to tell the compiler that, "I know that this method cannot actually be called with any type of event, but I want you to allow this check and only fail at runtime if the given argument is not of the proper type." then you can do that, assuming you actually have a way of ensuring you call each callback with the proper arguments. You can't just do a cast either; you need to wrap the method in a new method that does the cast:
eventTable.Add("test", e => callback((T)e));
You need to have the type parameter be part of the EventDispatcher class:
public class EventDispatcher<T> : where T : Event {
public Dictionary<string, MyCallback<T>> eventTable = new Dictionary<string, MyCallback<T>>();
void RegisterCallback(MyCallback<T> callback) {
eventTable.Add("test", callback);
}
}
This is because the MyCallback<Event> declared in eventTable is not going to be compiled into the same type declared in RegisteredCallback when written like your example.
I have a System.Reflection.MethodInfo and would like to have a method that creates a delegate(preferably a Func<...> or an Action<...>) that represents that method, given an instance to invoke it on.
So ideally I would like something like the following psuedo-code:
public TDelegate GetMethod<TDelegate>(MethodInfo methodToRepresent, object instanceToInvokeOn)
{
return (TDelegate)((parameters....) => methodToRepresent.Invoke(instanceToInvokeOn, all parameters in an object[]));
}
where TDelegate represents the signature of the represented method. If the signatures don't match, an exception should be thrown.
I realise I probably can't achieve this with a simple lambda expression, since its parametertypes must be known at compile-time. Perhaps I need to construct a delegate from scratch? Is it possible to create a delegate by specifying its body and parameters seperately?
Thank you
I don't really understand your question. But perhaps you want this:
public TDelegate GetMethod<TDelegate>(MethodInfo methodToRepresent, object instanceToInvokeOn)
where TDelegate:class
{
return (TDelegate)(object)Delegate.CreateDelegate(typeof(TDelegate), instanceToInvokeOn, methodToRepresent);
}
You can do this with the following method. Note that you can't really create a generic Action<...> using this method because, as you say, the types are not known at compile time. But this gets you pretty close.
public delegate void DynamicInvokeDelegate(params object[] args);
public static DynamicInvokeDelegate CreateDynamicInvokeDelegate(MethodInfo method, object instance) {
return args => method.Invoke(instance, args);
}
If you need the delegate to return a value:
public delegate object DynamicInvokeWithReturnDelegate(params object[] args);
public static DynamicInvokeWithReturnDelegate CreateDynamicInvokeWithReturnDelegate(MethodInfo method, object instance) {
return args => method.Invoke(instance, args);
}
EDIT:
It actually looks like you might be wanting this code:
public static T GetDelegate<T>(MethodInfo method, object instance)
where T : class
{
return (T)(object)Delegate.CreateDelegate(typeof(T), instance, method);
}
The (object) cast is required, since the compiler will not allow you to cast Delegate to any random type, and you cannot constrain T to be a delegate. Casting through object satisfies the compiler.
Is it possible to define a DynamicMethod with generic type parameters? The MethodBuilder class has the DefineGenericParameters method. Does the DynamicMethod have a counterpart? For example is it possible to create method with a signature like the one given blow using DynamicMethod?
void T Foo<T>(T a1, int a2)
This doesn't appear to be possible: as you've seen DynamicMethod has no DefineGenericParameters method, and it inherits MakeGenericMethod from its MethodInfo base class, which just throws NotSupportedException.
A couple of possibilities:
Define a whole dynamic assembly using AppDomain.DefineDynamicAssembly
Do generics yourself, by generating the same DynamicMethod once for each set of type arguments
Actually there is a way, it's not exactly generic but you'll get the idea:
public delegate T Foo<T>(T a1, int a2);
public class Dynamic<T>
{
public static readonly Foo<T> Foo = GenerateFoo<T>();
private static Foo<V> GenerateFoo<V>()
{
Type[] args = { typeof(V), typeof(int)};
DynamicMethod method =
new DynamicMethod("FooDynamic", typeof(V), args);
// emit it
return (Foo<V>)method.CreateDelegate(typeof(Foo<V>));
}
}
You can call it like this:
Dynamic<double>.Foo(1.0, 3);