it-roy-ru.com

Динамическое лямбда-выражение с динамическим параметром

Учитывая этот класс

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();
}

Возьму правую часть лямбда-выражения и верну мне делегата. Так что если это называется так:

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 динамически). Я предполагаю, что это потому, что вызов get foo.Bar должен быть динамически отправлен (используя Expression.Dynamic), но это не сработает для меня, потому что ключевой целью является то, что пользователь может ввести простое выражение и выполнить его. Является ли это возможным?

14
Ron Idaho

Я получил это работает, если это поможет вам:

Компилятор :

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 тоже немного болезнен, когда дело доходит до универсальных методов, поэтому мне пришлось создавать специфичные для типа методы доступа, которыене самые лучшие, но они работают.

1
Dan Rayson