制作 lambda 表达式以从泛型 class 调用方法
Making a lambda expression to call a method from a generic class
我有这些简单的界面:
public interface IQuery<TResult> { }
public interface IQueryHandler<in TQuery, out TResult>
where TQuery : IQuery<TResult> {
TResult Handle(TQuery query);
}
并且有一些实现。我正在尝试创建一个表达式树以在指定的处理程序上调用 Handle
方法。我的意思是:
var query = new MyQuery(); // which MyQuery implements IQuery<int>
object handler = _someServiceProvider
.Get<IQueryHandler<MyQuery, int>>();
还有一个MyQueryHandler
:
public class MyQueryHandler : IQueryHandler<MyQuery, int> {
public int Handle(MyQuery query) { return 20; }
}
现在,我正在尝试创建一个 Func<object, MyQuery, int>
来像这样调用:
var func = GetMethod<MyQuery, int>(handler);
var result = func(handler, query);
这是我的 GetMethod
实现:
private Func<object, TQuery, TResult> GetMethod<TQuery, TResult>(object obj)
where TQuery : IQuery<TResult> {
var methodInfo = obj.GetType().GetMethod(nameof(IQueryHandler<TQuery, TResult>.Handle));
var insExp = Expression.Parameter(typeof(object), "ins");
var inputExp = Expression.Parameter(typeof(TQuery), "query");
var instanceExp = Expression.Variable(obj.GetType(), "instance");
var assExp = Expression.Assign(instanceExp, Expression.Convert(insExp, obj.GetType()));
var castExp = Expression.Convert(inputExp, methodInfo.GetParameters()[0].ParameterType);
var callExp = Expression.Call(instanceExp, methodInfo, castExp);
var blockExp = Expression.Block(new Expression[] {
insExp,
inputExp,
instanceExp,
assExp,
castExp,
callExp
});
var func =
Expression.Lambda<Func<object, TQuery, TResult>>(
blockExp,
insExp,
inputExp).Compile();
return func;
}
但是,当我尝试编译 Lambda
时,出现此错误:
An exception of type 'System.InvalidOperationException' occurred in
System.Core.dll but was not handled in user code
Additional information: variable 'instance' of type
'Namespace.MyQueryHandler' referenced from scope '', but it is not
defined
我哪里错了?我错过了什么?你有什么主意吗?提前致谢。
不太清楚你在GetMethod
中试图用你的表达式做什么,所以我不会使用它并完全从头开始写。
如果您想将处理程序和查询都传递给您的方法 - 您不需要将任何实例传递给 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);
如果您 做 将 handler
实例传递给 GetMethod
- 您以后不需要再次将同一实例传递给您创建的 func
- 你可以像这样重用相同的实例(当然假设这适合你的场景):
private static Func<TQuery, TResult> GetMethod<TQuery, TResult>(object obj)
where TQuery : IQuery<TResult> {
// parameter
var query = Expression.Parameter(typeof(TQuery), "query");
// note Expression.Constant here - you use the same instance for every call
var body = Expression.Call(Expression.Constant(obj), "Handle", new Type[0], query);
return Expression.Lambda<Func<TQuery, TResult>>(body, query).Compile();
}
并使用它:
var query = new MyQuery(); // which MyQuery implements IQuery<int>
object handler = new MyQueryHandler();
var func = GetMethod<MyQuery, int>(handler);
var result = func(query);
据我所知,您正在尝试编写此函数:
TResult f(object ins, TQuery query)
{
var instance = (MyQueryHandler)ins;
return instance.Handle(query);
}
要使用表达式树执行此操作,您必须在 Expression.Block
中声明变量,但仅指定上面的两个语句,而不是所有子表达式:
var blockExp = Expression.Block(new[] { instanceExp }, new Expression[] {
assExp,
callExp
});
但更简单的选择是改为编写以下函数:
TResult f(object ins, TQuery query)
{
return ((MyQueryHandler)ins).Handle(query);
}
看起来像这样:
var callExp = Expression.Call(
Expression.Convert(insExp, obj.GetType()), methodInfo, castExp);
var func =
Expression.Lambda<Func<object, TQuery, TResult>>(
callExp,
insExp,
inputExp).Compile();
我有这些简单的界面:
public interface IQuery<TResult> { }
public interface IQueryHandler<in TQuery, out TResult>
where TQuery : IQuery<TResult> {
TResult Handle(TQuery query);
}
并且有一些实现。我正在尝试创建一个表达式树以在指定的处理程序上调用 Handle
方法。我的意思是:
var query = new MyQuery(); // which MyQuery implements IQuery<int>
object handler = _someServiceProvider
.Get<IQueryHandler<MyQuery, int>>();
还有一个MyQueryHandler
:
public class MyQueryHandler : IQueryHandler<MyQuery, int> {
public int Handle(MyQuery query) { return 20; }
}
现在,我正在尝试创建一个 Func<object, MyQuery, int>
来像这样调用:
var func = GetMethod<MyQuery, int>(handler);
var result = func(handler, query);
这是我的 GetMethod
实现:
private Func<object, TQuery, TResult> GetMethod<TQuery, TResult>(object obj)
where TQuery : IQuery<TResult> {
var methodInfo = obj.GetType().GetMethod(nameof(IQueryHandler<TQuery, TResult>.Handle));
var insExp = Expression.Parameter(typeof(object), "ins");
var inputExp = Expression.Parameter(typeof(TQuery), "query");
var instanceExp = Expression.Variable(obj.GetType(), "instance");
var assExp = Expression.Assign(instanceExp, Expression.Convert(insExp, obj.GetType()));
var castExp = Expression.Convert(inputExp, methodInfo.GetParameters()[0].ParameterType);
var callExp = Expression.Call(instanceExp, methodInfo, castExp);
var blockExp = Expression.Block(new Expression[] {
insExp,
inputExp,
instanceExp,
assExp,
castExp,
callExp
});
var func =
Expression.Lambda<Func<object, TQuery, TResult>>(
blockExp,
insExp,
inputExp).Compile();
return func;
}
但是,当我尝试编译 Lambda
时,出现此错误:
An exception of type 'System.InvalidOperationException' occurred in System.Core.dll but was not handled in user code
Additional information: variable 'instance' of type 'Namespace.MyQueryHandler' referenced from scope '', but it is not defined
我哪里错了?我错过了什么?你有什么主意吗?提前致谢。
不太清楚你在GetMethod
中试图用你的表达式做什么,所以我不会使用它并完全从头开始写。
如果您想将处理程序和查询都传递给您的方法 - 您不需要将任何实例传递给 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);
如果您 做 将 handler
实例传递给 GetMethod
- 您以后不需要再次将同一实例传递给您创建的 func
- 你可以像这样重用相同的实例(当然假设这适合你的场景):
private static Func<TQuery, TResult> GetMethod<TQuery, TResult>(object obj)
where TQuery : IQuery<TResult> {
// parameter
var query = Expression.Parameter(typeof(TQuery), "query");
// note Expression.Constant here - you use the same instance for every call
var body = Expression.Call(Expression.Constant(obj), "Handle", new Type[0], query);
return Expression.Lambda<Func<TQuery, TResult>>(body, query).Compile();
}
并使用它:
var query = new MyQuery(); // which MyQuery implements IQuery<int>
object handler = new MyQueryHandler();
var func = GetMethod<MyQuery, int>(handler);
var result = func(query);
据我所知,您正在尝试编写此函数:
TResult f(object ins, TQuery query)
{
var instance = (MyQueryHandler)ins;
return instance.Handle(query);
}
要使用表达式树执行此操作,您必须在 Expression.Block
中声明变量,但仅指定上面的两个语句,而不是所有子表达式:
var blockExp = Expression.Block(new[] { instanceExp }, new Expression[] {
assExp,
callExp
});
但更简单的选择是改为编写以下函数:
TResult f(object ins, TQuery query)
{
return ((MyQueryHandler)ins).Handle(query);
}
看起来像这样:
var callExp = Expression.Call(
Expression.Convert(insExp, obj.GetType()), methodInfo, castExp);
var func =
Expression.Lambda<Func<object, TQuery, TResult>>(
callExp,
insExp,
inputExp).Compile();