How to use generic types with type parameters?

3

I have a dictionary that serves as a type mapper with C #.

var Map = new Dictionary<Type, Type>()
{
    {typeof(A1), typeof(A2)},
    {typeof(B1), typeof(B2)},
    {typeof(C1), typeof(C2)},
    {typeof(D1), typeof(D2)}
};

I intend to use it as follows:

public virtual T Get<T>(int id) where T : CD.IDomainModel
{
    using (var rep = new R.Rep())
    {
        var t = PegarEquavalencia(typeof(T));

        var obj = rep.Get<t>(id);//Aqui aponta um erro no 't'

        return (T)Activator.CreateInstance(typeof(T), obj);
    }
}

public static Type PegarEquavalencia(Type type)
{
    Type tipoEquivalente;
    Map.TryGetValue(type, out tipoEquivalente);

    return tipoEquivalente;
}

The problem is that I have not yet been able to use this Type Map to be able to do this action. Basically what I want to do is pass an A1 type, take its equivalent in the dictionary, in case A2, and use the A2 type as a parameter for the generic type of the other method.

I'm using NHibernate, and the Rep class is a generic repository. The Get<T>(object id) method has the generic type to return the object I want to get in the database.

My solution basically has 3 projects:

  • One for database mapping, Domain, Mapping (xml) and Repository;
  • One for UI;
  • And the other to the business logic of the application.

In the project that encapsulates business logic, I have objects that will be used to exchange data with the UI.

I made a dictionary as above to map objects for UI communication with mapping objects with ORM.

What I want to do is to request an object of type A1, from the interface, there an intermediate method will check its equivalent in the dictionary, search the object with the equivalent type, A2, in the database, and return object A1 which receives with parameter the object A2;

    
asked by anonymous 22.04.2014 / 22:20

1 answer

4

You can do this via reflection.

To call the method using reflection, given a type t obtained from the dictionary:

public virtual T Get<T>(int id) where T : CD.IDomainModel
{
    using(var rep = new Rep())
    {
        var t = PegarEquavalencia(typeof(T));

        // chamando o método via reflexão... isso não é muito eficiente,
        // mas no caso citado, de acesso a banco de dados, talvez seja aceitável
        var obj = typeof(Rep)
            .GetMethod("Get", BindingFlags.Instance | BindingFlags.Public)
            .MakeGenericMethod(t)
            .Invoke(rep, new object[] { id });

        Activator.CreateInstance(typeof(T), obj);
    }
}

Note that this is inefficient, so it is advisable to create a delegate to do the desired action altogether, and use a type dictionary to delegate the desired action.

I'll post an example of how to do it most efficiently ... meanwhile you can use try as indicated, it will work!

Example compiling the delegate at the time, and caching

This example is in case you need more performance, if one day the reflection begins to weigh in the processing ... I say this, because the code is more complicated, and you will need a good excuse to use this monster:

private readonly ConcurrentDictionary<Type, object> cache
    = new ConcurrentDictionary<Type, object>();

public virtual T Get<T>(int id) where T : CD.IDomainModel
{
    var t2 = PegarEquavalencia(typeof(T));
    var getInternal = (Func<int, T>)cache
        .GetOrAdd(typeof(T), t => DelegateFor_GetInternal<T>(t2));
    var result = getInternal(id);
    return result;
}

private static Func<int, T> DelegateFor_GetInternal<T>(Type t2)
{
    var ctor = typeof(T).GetConstructor(new[] { t2 });

    var param = Expression.Parameter(t2, "obj");

    var creatorType = typeof(Func<,>).MakeGenericType(t2, typeof(T));

    dynamic lambda = typeof(Class2)
        .GetMethod("Lambda", BindingFlags.Static | BindingFlags.NonPublic)
        .MakeGenericMethod(creatorType)
        .Invoke(null, new object[]
        {
            Expression.New(ctor, param), new[] { param }
        });

    var creatorDelegate = (object)lambda.Compile();

    // func
    var getInternal = Activator.CreateInstance(typeof(GetInternal<,>)
        .MakeGenericType(t2, typeof(T)), creatorDelegate);

    var result = (Func<int, T>)Delegate.CreateDelegate(
        typeof(Func<int, T>),
        getInternal,
        getInternal.GetType().GetMethod("Invoke",
            BindingFlags.Public | BindingFlags.Instance));

    return result;
}

private static Expression<TDelegate> Lambda<TDelegate>(
    Expression body, ParameterExpression[] parameters)
{
    return Expression.Lambda<TDelegate>(body, parameters);
}

public class GetInternal<T2, T>
{
    private readonly Func<T2, T> creator;

    public GetInternal(Func<T2, T> creator)
    {
        this.creator = creator;
    }

    public T Invoke(int id)
    {
        using (var rep = new R.Rep())
        {
            var obj = rep.Get<T2>(id);
            return creator(obj);
        }
    }
}

public static Type PegarEquavalencia(Type type)
{
    Type tipoEquivalente;
    Map.TryGetValue(type, out tipoEquivalente);

    return tipoEquivalente;
}
    
22.04.2014 / 22:37