问题
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>
回答1:
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);
回答2:
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.
来源:https://stackoverflow.com/questions/21681695/create-a-generic-func-delegate-using-a-runtime-type