构造.NET表达式以调用动态对象的正确方法

Dav*_*rdi 5 .net c# lambda dynamic

我需要创建一个System.Linq.Expressions.Expression调用动态对象.动态对象可以是ExpandoObject任何其他对象IDynamicMetaObjectProvider.

考虑以下测试:

var myInstance = DateTime.Now;

var methodInfo = myInstance.GetType().GetMethod("ToUniversalTime");

var methodCallExpression = Expression.Call(Expression.Constant(myInstance), methodInfo);
var expression = Expression.Lambda(methodCallExpression);

Assert.AreEqual(myInstance.ToUniversalTime(), expression.Compile().DynamicInvoke());
Run Code Online (Sandbox Code Playgroud)

我需要在声明myInstance时创建一个等效表达式(仅作为示例):

dynamic myInstance = new ExpandoObject();
myInstance.MyMethod = new Func<string>(() => "hello world");
Run Code Online (Sandbox Code Playgroud)

我想我需要使用Expression.Dynamic方法(参见MSDN).但我不知道如何使用它.我试图在谷歌搜索,但我发现的唯一的例子使用无法正式使用的Microsoft.CSharp.RuntimeBinder.Binder类(请参阅MSDN):

此API支持.NET Framework基础结构,不能直接在您的代码中使用.

使用Microsoft.CSharp.RuntimeBinder.Binder我可以编写下面的代码:

dynamic myInstance = new ExpandoObject();
myInstance.MyMethod = new Func<string>(() => "hello world");

var binder = Binder.InvokeMember(
    CSharpBinderFlags.None,
    "MyMethod",
    null,
    this.GetType(),
    new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant, null) });

var methodCallExpression = Expression.Dynamic(binder, typeof(object), Expression.Constant(myInstance));
var expression = Expression.Lambda(methodCallExpression);

Assert.AreEqual(myInstance.MyMethod(), expression.Compile().DynamicInvoke());
Run Code Online (Sandbox Code Playgroud)

这个解决方案对吗?

Jea*_*nal 1

这里有一个问题:根据我的理解,在没有某种Binder对象的情况下进行动态调用是没有意义的。

活页夹对象表示动态名称解析遵循的规则:

  • 匹配应该区分大小写吗?
  • 方法重载应该如何解决?
  • 如果对象是非动态对象怎么办?
  • 你使用什么后备方案?

换句话说,Binder对象代表调用“语言”的语义,而IDynamicMetaObjectProvider代表被调用对象的语义。

所以,是的,我们不应该使用 CSharp Binder 对象。尤其是当发生一些只能通过使用对象内部来解决的问题时,这种感觉尤其明显。然而,替代方案只是使用另一个非框架提供的Binder实现。