如何使用表达式树调用带有 EventHandler 参数的方法?
How to call method with EventHandler parameter using expression trees?
考虑这段简单的代码。 如何使用表达式树完成此操作?
ErrorsChangedEventManager.AddHandler(obj, obj.SomeHandler);
这是一个小示例,说明了我要完成的工作。 (添加对 WindowBase
的引用以使其编译。)
class Program : INotifyDataErrorInfo
{
public int Id { get; set; }
static void Main(string[] args)
{
var p1 = new Program { Id = 1 };
var p2 = new Program { Id = 2 };
// Here is the root of the problem.
// I need to do this INSIDE the expression from a given instance of Program.
EventHandler<DataErrorsChangedEventArgs> handler = p1.OnError;
var handlerConstant = Expression.Constant(handler);
var mi = typeof(ErrorsChangedEventManager).GetMethod(nameof(ErrorsChangedEventManager.AddHandler),
BindingFlags.Public | BindingFlags.Static);
var source = Expression.Parameter(typeof(INotifyDataErrorInfo), "source");
var program = Expression.Parameter(typeof(Program), "program");
// This will work, but the OnError method will be invoked on the wrong instance.
// So, I need to get the expression to perform what would otherwise be easy in code...
// E.g. AddHandler(someObject, p2.OnError);
var call = Expression.Call(mi, source, handlerConstant);
var expr = Expression.Lambda<Action<INotifyDataErrorInfo, Program>>(call, source, program);
var action = expr.Compile();
action.DynamicInvoke(p1, p2);
p1.ErrorsChanged.Invoke(p1, new DataErrorsChangedEventArgs("Foo"));
}
void OnError(object sender, DataErrorsChangedEventArgs e)
{
if (sender is Program p)
{
Console.WriteLine($"OnError called for Id={Id}. Expected Id=2");
}
}
public IEnumerable GetErrors(string propertyName) => Enumerable.Empty<string>();
public bool HasErrors => false;
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
}
显然,它不起作用。我不知何故需要将 OnError
处理程序作为 参数 提供给调用。
看起来最简单的事情是创建一个 lambda 为您创建 EventHandler<DataErrorsChangedEventArgs>
,然后使用 Expression.Invoke
调用它:
public class Program : INotifyDataErrorInfo
{
public int Id { get; set; }
public static void Main()
{
var p1 = new Program { Id = 1 };
var p2 = new Program { Id = 2 };
var mi = typeof(ErrorsChangedEventManager).GetMethod(nameof(ErrorsChangedEventManager.AddHandler),
BindingFlags.Public | BindingFlags.Static);
var source = Expression.Parameter(typeof(INotifyDataErrorInfo), "source");
var program = Expression.Parameter(typeof(Program), "program");
Expression<Func<Program, EventHandler<DataErrorsChangedEventArgs>>> createDelegate = p => p.OnError;
var createDelegateInvoke = Expression.Invoke(createDelegate, program);
var call = Expression.Call(mi, source, createDelegateInvoke);
var expr = Expression.Lambda<Action<INotifyDataErrorInfo, Program>>(call, source, program);
var action = expr.Compile();
action(p1, p2);
p1.ErrorsChanged.Invoke(p1, new DataErrorsChangedEventArgs("Foo"));
}
public void OnError(object sender, DataErrorsChangedEventArgs e)
{
if (sender is Program p)
{
Console.WriteLine($"OnError called for Id={Id}. Expected Id=2");
}
}
public IEnumerable GetErrors(string propertyName) => Enumerable.Empty<string>();
public bool HasErrors => false;
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
}
如果您查看 createDelegate
的 DebugView,您可以看到编译器创建了:
.Lambda #Lambda1<System.Func`2[Program,System.EventHandler`1[System.ComponentModel.DataErrorsChangedEventArgs]]>(Program $p)
{
(System.EventHandler`1[System.ComponentModel.DataErrorsChangedEventArgs]).Call .Constant<System.Reflection.MethodInfo>(Void OnError(System.Object, System.ComponentModel.DataErrorsChangedEventArgs)).CreateDelegate(
.Constant<System.Type>(System.EventHandler`1[System.ComponentModel.DataErrorsChangedEventArgs]),
$p)
}
如果需要,您可以自己构造此表达式,方法是获取 OnError
的 MethodInfo
,然后对其调用 CreateDelegate
。
综上所述,您只需使用 lambda 即可完成所有这些操作:
Expression<Action<INotifyDataErrorInfo, Program>> test = (source, program) =>
ErrorsChangedEventManager.AddHandler(source, program.OnError);
考虑这段简单的代码。 如何使用表达式树完成此操作?
ErrorsChangedEventManager.AddHandler(obj, obj.SomeHandler);
这是一个小示例,说明了我要完成的工作。 (添加对 WindowBase
的引用以使其编译。)
class Program : INotifyDataErrorInfo
{
public int Id { get; set; }
static void Main(string[] args)
{
var p1 = new Program { Id = 1 };
var p2 = new Program { Id = 2 };
// Here is the root of the problem.
// I need to do this INSIDE the expression from a given instance of Program.
EventHandler<DataErrorsChangedEventArgs> handler = p1.OnError;
var handlerConstant = Expression.Constant(handler);
var mi = typeof(ErrorsChangedEventManager).GetMethod(nameof(ErrorsChangedEventManager.AddHandler),
BindingFlags.Public | BindingFlags.Static);
var source = Expression.Parameter(typeof(INotifyDataErrorInfo), "source");
var program = Expression.Parameter(typeof(Program), "program");
// This will work, but the OnError method will be invoked on the wrong instance.
// So, I need to get the expression to perform what would otherwise be easy in code...
// E.g. AddHandler(someObject, p2.OnError);
var call = Expression.Call(mi, source, handlerConstant);
var expr = Expression.Lambda<Action<INotifyDataErrorInfo, Program>>(call, source, program);
var action = expr.Compile();
action.DynamicInvoke(p1, p2);
p1.ErrorsChanged.Invoke(p1, new DataErrorsChangedEventArgs("Foo"));
}
void OnError(object sender, DataErrorsChangedEventArgs e)
{
if (sender is Program p)
{
Console.WriteLine($"OnError called for Id={Id}. Expected Id=2");
}
}
public IEnumerable GetErrors(string propertyName) => Enumerable.Empty<string>();
public bool HasErrors => false;
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
}
显然,它不起作用。我不知何故需要将 OnError
处理程序作为 参数 提供给调用。
看起来最简单的事情是创建一个 lambda 为您创建 EventHandler<DataErrorsChangedEventArgs>
,然后使用 Expression.Invoke
调用它:
public class Program : INotifyDataErrorInfo
{
public int Id { get; set; }
public static void Main()
{
var p1 = new Program { Id = 1 };
var p2 = new Program { Id = 2 };
var mi = typeof(ErrorsChangedEventManager).GetMethod(nameof(ErrorsChangedEventManager.AddHandler),
BindingFlags.Public | BindingFlags.Static);
var source = Expression.Parameter(typeof(INotifyDataErrorInfo), "source");
var program = Expression.Parameter(typeof(Program), "program");
Expression<Func<Program, EventHandler<DataErrorsChangedEventArgs>>> createDelegate = p => p.OnError;
var createDelegateInvoke = Expression.Invoke(createDelegate, program);
var call = Expression.Call(mi, source, createDelegateInvoke);
var expr = Expression.Lambda<Action<INotifyDataErrorInfo, Program>>(call, source, program);
var action = expr.Compile();
action(p1, p2);
p1.ErrorsChanged.Invoke(p1, new DataErrorsChangedEventArgs("Foo"));
}
public void OnError(object sender, DataErrorsChangedEventArgs e)
{
if (sender is Program p)
{
Console.WriteLine($"OnError called for Id={Id}. Expected Id=2");
}
}
public IEnumerable GetErrors(string propertyName) => Enumerable.Empty<string>();
public bool HasErrors => false;
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
}
如果您查看 createDelegate
的 DebugView,您可以看到编译器创建了:
.Lambda #Lambda1<System.Func`2[Program,System.EventHandler`1[System.ComponentModel.DataErrorsChangedEventArgs]]>(Program $p)
{
(System.EventHandler`1[System.ComponentModel.DataErrorsChangedEventArgs]).Call .Constant<System.Reflection.MethodInfo>(Void OnError(System.Object, System.ComponentModel.DataErrorsChangedEventArgs)).CreateDelegate(
.Constant<System.Type>(System.EventHandler`1[System.ComponentModel.DataErrorsChangedEventArgs]),
$p)
}
如果需要,您可以自己构造此表达式,方法是获取 OnError
的 MethodInfo
,然后对其调用 CreateDelegate
。
综上所述,您只需使用 lambda 即可完成所有这些操作:
Expression<Action<INotifyDataErrorInfo, Program>> test = (source, program) =>
ErrorsChangedEventManager.AddHandler(source, program.OnError);