How to use generic types with type parameters?

Asked

Viewed 2,186 times

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 haven’t been able to use this Type Map yet to do this action. Basically what I want to do is pass a type A1, take the equivalent of it in the dictionary, in case A2, and use the type of A2 as a parameter for the generic type of the other method.

I am using Nhibernate, and the Rep class is a generic repository. The method Get<T>(object id) is has the generic type to return the object I wish to pick up in the bank.

My solution has basically 3 projects:

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

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

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

What I intend to do is ask for an object of type A1, of the interface, then an intermediate method will check its equivalent in the dictionary, will search the object with the equivalent type, A2, in the database, and will return the object A1 that receives with parameter the object A2;

  • I don’t understand. What is the parameter for Id?

  • 1

    The @Ciganomorrisonmendez is right:" to which the Id?" You could use a sample code that works?

1 answer

4


It is possible to do this through 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 the recommended is to create a delegate to do the desired action, altogether, and to use a dictionary of types for intended action delegates.

I’m going to post an example of how to do it the most efficiently... while you can try it the right way, which will work!

Example compiling delegate on time, and caching

This example is in case you need more performance, if one day the reflection starts 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;
}
  • So I edited the post and put more details on my question. Regarding what you said about killing the utility of generic classes, this is exactly what I do not want to do, because for now I am working with 29 tables, and 29 equivalent classes, and with a great forecast of increase. If I have to do something specific for each one there will be a lot of work.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.