我试图弄清楚如何从代表动作“语句”行的字符串集合中生成动作...
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类型的东西,目前我无法深入了解认为这可以帮助其他人确定我要去哪里,从而帮助我找到更完整的解决方案...
因此,正如我在评论中解释的那样,这只是我的工作流引擎工作原理的一小部分,其思想是执行活动,然后作为内部引擎的一部分,它生成此Action来将计算值复制到下一个活动之前执行。
我有这个结构...
struct PropertySourceInfo
{
public Activity Source { get; set; }
public Activity Destination { get; set; }
public Link Link { get; set; }
}
以下代码中的“ SourceInfoFor(activity,p)”返回哪个,而选择块是我问题的根本原因...
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;
}
请注意
对于堆栈在提出问题时需要我们的外形,我觉得提出完整的问题域太大了,因此尽管这个问题在这种情况下可以通过其他方式解决,但我还是需要通过外部方式解决问题原因。
我也希望并希望对表达树有更深的了解!
答案 0 :(得分:1)
因此,事实证明,答案并不像我希望的那么简单。 简而言之...我需要编写一个表达式解析器。
对于最简单的情况(该问题所在的情况),我可以在部分解决方案中使用代码,但是对于完整的解决方案,我将必须构建一个表达式解析器,该解析器可以处理更多的复杂性到字符串。
就我而言,使用词典或类似方法只能解决一部分潜在问题,而我不能使用反思,因为我的处境需要“大规模重复使用已编译的操作”(我曾在此稍作提及)问题)。
我可以参考一系列问题的答案来解决该问题的各个方面,但是我设法找到了一个更“完整”的起点,这是我在其他地方想要实现的目标...
https://archive.codeplex.com/?p=simproexpr
...这个示例不仅可以解析表达式,还可以解析表达式块。
使用这样的东西或类似的东西,我将按照这些思路来解决我的问题,希望对其他人有帮助。