代码之家  ›  专栏  ›  技术社区  ›  War

用表达式作为字符串生成操作

  •  0
  • War  · 技术社区  · 6 年前

    我试图找出如何从字符串集合中生成一个动作,这些字符串表示动作“语句”行。。。

    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属性(hense The mapping)。

    最新情况

    所以我有一个部分的解决方案,虽然这是做了大量的假设,我想删除,它只映射{destination}.Foo={source}.Bar类型的东西,目前不能再深入了,我想这可能有助于其他人确定我的方向,从而帮助我找到一个更完整的解决方案。。。

    因此,正如我在注释中所解释的,这是我的工作流引擎工作原理的一小部分,其思想是执行活动,然后作为内部引擎的一部分,它生成此操作,以便在执行之前将计算值复制到下一个活动。

    我有这个结构。。。

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

    请注意

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

    我也喜欢并希望对表情树有更深的了解!

    1 回复  |  直到 6 年前
        1
  •  1
  •   War    6 年前

    结果发现,答案并不像我所希望的那么简单。 简而言之。。。我需要写一个表达式解析器。

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

    在我的例子中,使用字典或类似的方法只解决了一个潜在的问题,我不能使用反射,因为我的情况需要“大规模地重用编译的操作”(我在这个问题中稍微提到过)。

    我可以参考一系列问题的答案来解决这一问题的各个部分,但我设法找到了一个更“完整”的起点,来实现我在其他地方试图实现的目标。。。

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

    ... 这个例子不仅仅是解析表达式,它还能够解析表达式块。

    利用这个/类似的东西,我将沿着这些思路来解决我的问题,我希望这能帮助其他人。