鉴于这个类

public class Foo
{
    public string Name { get; set; }
}

此方法(在其他一些类中)...
private Func<Foo, string> Compile(string body)
{
    ParameterExpression prm = Expression.Parameter(typeof(Foo), "foo");
    LambdaExpression exp = DynamicExpressionParser.ParseLambda(new[] { prm }, typeof(string), body);
    return (Func<Foo, string>)exp.Compile();
}

将使用lambda表达式的右手边,并给我回一个代表。因此,如果这样调用它:
Foo f = new Foo { Name = "Hamilton Academicals" };
//foo => foo.Name.Substring(0,3)
Func<Foo, string> fn = Compile("foo.Name.Substring(0,3)");
string sub = fn(f);

然后,sub将具有值“Ham”。

很好,但是,我想使Foo成为DynamicObject的子类(以便我可以实现TryGetMember以动态计算属性值),因此我想采用该表达式并获得与之等效的表达式
Func<dynamic, dynamic> fn = foo => foo.Name.Substring(0,3);

我已经尝试使用自定义Expression.Dynamic尝试CallSiteBinder,但是由于Bar类型中不存在属性或字段Object而失败(当我尝试动态访问foo.Bar时)。我假设这是因为获取foo.Bar的调用需要动态地调度(使用Expression.Dynamic),但这对我来说不起作用,因为一个主要目的是用户可以输入一个简单的表达式并执行它。是否可以?

最佳答案

我可以解决这个问题,如果可以帮助您:

编译器:

using System;
using System.Linq.Dynamic.Core;
using System.Linq.Expressions;


namespace ConsoleApp4
{
    public class Compiler
    {
        public static Func<CustomDynamic, TResult> Compile<TResult>(string body)
        {
            ParameterExpression prm = Expression.Parameter(typeof(CustomDynamic), typeof(CustomDynamic).Name);
            LambdaExpression exp = DynamicExpressionParser.ParseLambda(new[] { prm }, typeof(TResult), body);
            return (Func<CustomDynamic, TResult>)exp.Compile();
        }
    }
}

动态对象:
using System.Collections.Generic;
using System.Dynamic;

namespace ConsoleApp4
{
    public class CustomDynamic
    {
        ExpandoObject _values;
        public CustomDynamic()
        {
            _values = new ExpandoObject();
        }

        public void AddProperty(string propertyName, object propertyValue)
        {
            var expandoDict = _values as IDictionary<string, object>;
            if (expandoDict.ContainsKey(propertyName))
                expandoDict[propertyName] = propertyValue;
            else
                expandoDict.Add(propertyName, propertyValue);
        }

        public string GetString(string propertyName)
        {
            var expandoDict = _values as IDictionary<string, object>;
            if (expandoDict.ContainsKey(propertyName))
                return (string)expandoDict[propertyName];
            else
                throw new KeyNotFoundException($"dynamic object did not contain property {propertyName}");
        }
        public int GetInt(string propertyName)
        {
            var expandoDict = _values as IDictionary<string, object>;
            if (expandoDict.ContainsKey(propertyName))
                return (int)expandoDict[propertyName];
            else
                throw new KeyNotFoundException($"dynamic object did not contain property {propertyName}");
        }
    }
}

用例:
using System;

namespace ConsoleApp4
{
    class Program
    {
        static void Main(string[] args)
        {
            CustomDynamic f = new CustomDynamic();
            f.AddProperty("Name", "Hamiltonian Physics");

            Func<CustomDynamic, string> fn = Compiler.Compile<string>("CustomDynamic.GetString(\"Name\").SubString(0, 3)");

            string sub = fn(f);

            Console.WriteLine(sub);
            Console.ReadLine();
        }
    }
}

它只是利用了ExpandoObject类。不幸的是,您需要通过其API创建对象,才能为每个属性创建对象,例如“AddProperty”,但是嘿嘿。

当涉及到通用方法时,DynamicExpressionParser.ParseLambda也有些痛苦,因此我不得不创建特定于类型的访问器,并不是最好的,但是它可以正常工作。

关于c# - 具有动态参数的动态Lambda表达式,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48888471/

10-16 00:24