Créer une expression lambda pour appeler une méthode depuis une classe générique

c# c#-4.0 expression-trees func lambda

Question

J'ai ces interface simple:

public interface IQuery<TResult> { }

public interface IQueryHandler<in TQuery, out TResult> 
    where TQuery : IQuery<TResult> {
    TResult Handle(TQuery query);
}

Et il y en a quelques implémentation. J'essaie de créer une arborescence d'expression pour appeler la méthode Handle sur un gestionnaire spécifié. Je veux dire:

public interface IQuery<TResult> { }

public interface IQueryHandler<in TQuery, out TResult> 
    where TQuery : IQuery<TResult> {
    TResult Handle(TQuery query);
}

En outre, il existe un MyQueryHandler :

public interface IQuery<TResult> { }

public interface IQueryHandler<in TQuery, out TResult> 
    where TQuery : IQuery<TResult> {
    TResult Handle(TQuery query);
}

Maintenant, j'essaie de créer un Func<object, MyQuery, int> à appeler comme ceci:

public interface IQuery<TResult> { }

public interface IQueryHandler<in TQuery, out TResult> 
    where TQuery : IQuery<TResult> {
    TResult Handle(TQuery query);
}

Et voici mon implémentation GetMethod :

public interface IQuery<TResult> { }

public interface IQueryHandler<in TQuery, out TResult> 
    where TQuery : IQuery<TResult> {
    TResult Handle(TQuery query);
}

Mais, lorsque j'essaie de compiler Lambda , j'obtiens cette erreur:

Une exception de type 'System.InvalidOperationException' s'est produite dans System.Core.dll mais n'a pas été gérée dans le code utilisateur

Informations complémentaires: variable 'instance' de type 'Namespace.MyQueryHandler' référencée depuis le périmètre '', mais non définie

Où est-ce que je me trompe? Qu'est-ce qui m'a manqué? Avez-vous une idée? Merci d'avance.

Réponse acceptée

Autant que je sache, vous essayez d'écrire cette fonction:

TResult f(object ins, TQuery query)
{
    var instance = (MyQueryHandler)ins;
    return instance.Handle(query);
}

Pour cela, vous devez déclarer la variable dans votre Expression.Block , mais ne spécifier que les deux instructions ci-dessus, pas toutes les sous-expressions:

TResult f(object ins, TQuery query)
{
    var instance = (MyQueryHandler)ins;
    return instance.Handle(query);
}

Mais une option plus simple consisterait à écrire la fonction suivante:

TResult f(object ins, TQuery query)
{
    var instance = (MyQueryHandler)ins;
    return instance.Handle(query);
}

Cela ressemblerait à ceci:

TResult f(object ins, TQuery query)
{
    var instance = (MyQueryHandler)ins;
    return instance.Handle(query);
}

Réponse populaire

Ce que vous essayez de faire avec votre expression dans GetMethod n’est pas vraiment clair. Je ne vais donc pas l’utiliser et écrire complètement à partir de rien.

Si vous souhaitez transmettre le gestionnaire et la requête à votre méthode, vous n'avez pas besoin de transmettre d'instance à GetMethod :

private static Func<object, TQuery, TResult> GetMethod<TQuery, TResult>()
    where TQuery : IQuery<TResult> {
    // "query" paramter
    var query = Expression.Parameter(typeof(TQuery), "query");
    // "handler" parameter
    var handler = Expression.Parameter(typeof(object), "handler");
    // convert your "object" parameter to handler type (not type safe of course)
    // ((IQueryHandler<TQuery, TResult>) handler).Handle(query)
    var body = Expression.Call(Expression.Convert(handler, typeof(IQueryHandler<TQuery, TResult>)), "Handle", new Type[0], query);
    //(handler, query) => ((IQueryHandler<TQuery, TResult>) handler).Handle(query);
    return Expression.Lambda<Func<object, TQuery, TResult>>(body, handler, query).Compile();
}

object handler = new MyQueryHandler();
var func = GetMethod<MyQuery, int>();
var result = func(handler, query);

Si vous faites passer handler instance à GetMethod - vous n'avez pas besoin de passer plus tard cette même instance de nouveau à votre créé func - vous pouvez réutiliser la même instance comme celui - ci ( en supposant que votre scénario correspond bien sûr):

private static Func<object, TQuery, TResult> GetMethod<TQuery, TResult>()
    where TQuery : IQuery<TResult> {
    // "query" paramter
    var query = Expression.Parameter(typeof(TQuery), "query");
    // "handler" parameter
    var handler = Expression.Parameter(typeof(object), "handler");
    // convert your "object" parameter to handler type (not type safe of course)
    // ((IQueryHandler<TQuery, TResult>) handler).Handle(query)
    var body = Expression.Call(Expression.Convert(handler, typeof(IQueryHandler<TQuery, TResult>)), "Handle", new Type[0], query);
    //(handler, query) => ((IQueryHandler<TQuery, TResult>) handler).Handle(query);
    return Expression.Lambda<Func<object, TQuery, TResult>>(body, handler, query).Compile();
}

object handler = new MyQueryHandler();
var func = GetMethod<MyQuery, int>();
var result = func(handler, query);

Et utilisez-le:

private static Func<object, TQuery, TResult> GetMethod<TQuery, TResult>()
    where TQuery : IQuery<TResult> {
    // "query" paramter
    var query = Expression.Parameter(typeof(TQuery), "query");
    // "handler" parameter
    var handler = Expression.Parameter(typeof(object), "handler");
    // convert your "object" parameter to handler type (not type safe of course)
    // ((IQueryHandler<TQuery, TResult>) handler).Handle(query)
    var body = Expression.Call(Expression.Convert(handler, typeof(IQueryHandler<TQuery, TResult>)), "Handle", new Type[0], query);
    //(handler, query) => ((IQueryHandler<TQuery, TResult>) handler).Handle(query);
    return Expression.Lambda<Func<object, TQuery, TResult>>(body, handler, query).Compile();
}

object handler = new MyQueryHandler();
var func = GetMethod<MyQuery, int>();
var result = func(handler, query);



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi