5

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>

Community
  • 1
  • 1
Paul Chernoch
  • 5,275
  • 3
  • 52
  • 73

2 Answers2

0

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);
Matthew Abbott
  • 60,571
  • 9
  • 104
  • 129
0

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.

Paul Chernoch
  • 5,275
  • 3
  • 52
  • 73