使用表达式作为字符串生成操作 <T>

Generating Action<T> with Expressions as strings

我正在尝试弄清楚如何从代表动作 "statement" 行的字符串集合中生成动作 ...

using System.Linq.Dynamic;

Action<T> BuildAction<T>(T sourceObject, T destinationObject) where T : BaseThing
{
    var source = Expression.Parameter(sourceObject.GetType(), "source");
    var destination = Expression.Parameter(destinationObject.GetType(), "destination");

    var statements = new[] {
        "destination.Foo = source.Foo",
        "destination.X = source.Y"
    };

    var parsedStatements = statements.Select(s => DynamicExpression.Parse(new[] { destination, source }, typeof(void), s);

    return Expression.Lambda<Action<T>>(Expression.Block(parsedStatements));
}

最终的想法是……

Action<T> result = (destination, source) => {
     destination.Foo = source.Foo;
     destination.X = source.Y;
};

我遇到的另一个问题是源和目标不必是同一类型,它们只共享一个基本类型,所以在这个例子中目标可能没有 Y 属性 而源可能没有 X 属性(映射)。

一个更新

所以我有一个部分解决方案,尽管这是我想删除的大量假设并且它只映射 {destination}.Foo = {source}.Bar 类型的东西并且不能在那一刻我认为这可能会帮助其他人确定我的目标,从而帮助我找到更完整的解决方案...

正如我在评论中解释的那样,这是我的工作流引擎工作方式的一小部分,想法是执行活动,然后作为内部引擎的一部分,它生成此操作以将计算值复制到下一个activity 执行前。

我有这个结构...

struct PropertySourceInfo
{
    public Activity Source { get; set; }
    public Activity Destination { get; set; }
    public Link Link { get; set; }
}

下面代码中 "SourceInfoFor(activity, p)" 返回的 select 块是我问题的根本原因...

Action<Activity> BuildAssign(Activity activity, Flow flow)
{
    var type = activity.GetType();
    var destination = Expression.Parameter(typeof(Activity), "activity");

    // build property mappings
    var assigns = type.GetProperties()
        .Where(p => IsPreviousActivityInput(activity, p))
        .Select(p => {
            var info = SourceInfoFor(activity, p, flow);
            if (info != null)
            {
                var i = info.Value;
                var sidx = activity.Previous.IndexOf(sa => sa == i.Source);
                var sType = activity.Previous[sidx].GetType().GetCSharpTypeName();

                // ok my assumption here is that I have something like this ...
                // {destination}.Property = {Source}.Property
                // ... so breaking it up I can then build the Expression needed for each part: 
                var assignParts = i.Link.Expression.Split(' ');

                //TODO: do this more intelligently to handle "sub property value passing"
                var destExpr = Expression.Property(Expression.Convert(destination, type), assignParts[0].Split(".".ToCharArray()).Last());
                var destArray = Expression.Property(destination, type, "Previous");
                var sourceActivity = Expression.ArrayAccess(destArray, Expression.Constant(sidx));
                var sourceExpr = Expression.Property(Expression.Convert(sourceActivity, activity.Previous[sidx].GetType()), assignParts[2].Split(".".ToCharArray()).Last());

                var result = Expression.Assign(destExpr, sourceExpr);
                return result;
            }
            else
                return null;
        })
        .Where(i => i != null)
        .ToArray();

    // the complete block as a single "Action<TActivity>" that can be called
    if (assigns.Any())
    {
        var result = Expression.Lambda<Action<Activity>>(Expression.Block(assigns), destination);
        log.Debug(result.ToString());
        return result.Compile();
    }
    else
        return null;
}

请注意

对于堆栈在提出问题时要求我们的形式因素,我觉得提出我的完整问题域是一个太大的问题,所以虽然这个问题在这种情况下可以通过其他方式解决,但我需要以这种方式解决问题原因之外。

我也喜欢,也想深入了解表达式树!

原来这个问题的答案并不像我希望的那么简单。 简而言之...我需要编写一个表达式解析器。

对于最简单的情况(问题中提出的情况),我可以在我的部分解决方案中使用代码,但对于完整的解决方案,我将不得不构建一个可以处理更多复杂性的表达式解析器放入琴弦。

就我而言,使用字典或类似方法只能解决一部分潜在问题,我不能使用反射,因为我的情况需要"reuse of the compiled action at scale"(我在问题中略微提到了这一点) .

我可以参考问题列表和解决这个问题的各个部分的答案,但我设法找到了更多 "complete" 的起点,以实现我在其他地方试图实现的目标......

https://archive.codeplex.com/?p=simproexpr

...这个例子不仅仅可以解析表达式,它还可以解析表达式块。

使用那个/类似的东西我将按照这些思路构建一些东西来解决我的问题,我希望这对其他人有帮助。