【发布时间】:2012-01-31 15:51:46
【问题描述】:
我有一个过于复杂的二叉表达式树构建系统 它接受一个字符串和一对对象(玩家和世界)
树上的每个节点代表一个外部函数,它接受一个字符串、玩家和世界,并返回一个布尔值(用于测试)、一个字符串(用于输出)或 void(用于动作)
我的问题有三个:
首先,我需要使用Expression.Condition 或Expression.IfThenElse 之类的东西,其中测试表达式的形式为Expression<func<string, Player, World, bool>> 而不是Expresson<bool>(因为Expression.And 会输出)
其次,我需要确保 Player 和 World 的内存引用始终保持不变 - 这样如果树中的一个节点更新了 Player 中的某些内容,那么它仍会在下一个节点处更新。
最后我需要将所有的字符串一个接一个地追加。
如果我可以对树进行硬编码,它最终可能看起来像这样:
class Main
{
string Foo(string text, World world, Player player)
{
string output;
output += SomeClass.PrintStarting();
if (SomeClass.Exists(text, world, player))
{
output += SomeClass.PrintName(text, world, player);
SomeClass.KillPlayer(text, world, player);
if (SomeClass.Exists(text, world, player))
output += SomeClass.PrintSurvived(text, world, player);
}
else
output += SomeClass.PrintNotExists(text, world, player);
return output;
}
}
public class SomeClass
{
string PrintStart(string text, World world, Player player)
{
return "Starting.\n";
}
bool Exists(string text, World world, Player player)
{
player.Lives;
}
string PrintName(string text, World world, Player player)
{
return player.Name + ".\n";
}
string PrintSurvived(string text, World world, Player player)
{
return player.Name + "died.\n";
}
string PrintNotExists(string text, World world, Player player)
{
return "This person does not exist.\n";
}
void KillPlayer(string text, World world, Player player)
{
if (text != "kidding")
player.Lives = false;
}
}
进一步阐述:
我有一个 SomeClass 的实例及其所有测试/分配/字符串方法。
然后我去创建一个Expression<func<string[], World, Player, bool>>、Expression<Action<string[], World, Player>> 和Expression<func<string[], World, Player, string>> 的列表,并开始将它们一起放入表达式树中。
我处理过的事情的实际顺序(例如):
public string Foo2(string text, World world, Player player)
{
ParameterExpression result = Expression.Parameter(typeof(string), "result");
ParameterExpression inputString = Expression.Parameter(typeof(string[]), "inputString");
ParameterExpression inputWorld = Expression.Parameter(typeof(World), "inputWorld");
ParameterExpression inputPlayer = Expression.Parameter(typeof(Player), "inputPlayer");
System.Reflection.MethodInfo methodInfo = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string) });
Expression textPrintStarting = (Expression<Func<string, World, Player, string>>)((Text, World, Player) => SomeClass.PrintStarting(Text, World, Player));
Expression testExists = (Expression<Func<string, World, Player, bool>>)((Text, World, Player) => SomeClass.Exists(Text, World, Player));
Expression textPrintName = (Expression<Func<string, World, Player, string>>)((Text, World, Player) => SomeClass.PrintName(Text, World, Player));
Expression killPlayer = (Expression<Action<string, World, Player>>)((Text, World, Player) => SomeClass.KillPlayer(Text, World, Player));
Expression textPrintSurvived = (Expression<Func<string, World, Player, string>>)((Text, World, Player) => SomeClass.PrintSurvived(Text, World, Player));
Expression textPrintNotExist = (Expression<Func<string, World, Player, string>>)((Text, World, Player) => SomeClass.PrintNotExists(Text, World, Player));
Expression innerTest =
Expression.Condition(
Expression.Invoke(Expression.Lambda<Func<string, World, Player, bool>>(testExists, inputString, inputWorld, inputPlayer)),
Expression.Assign(result, Expression.Call(methodInfo, result, Expression.Lambda<Func<string, World, Player, string>>(textPrintSurvived, inputString, inputWorld, inputPlayer))),
Expression.Empty());
Expression success =
Expression.Block(
Expression.Assign(result, Expression.Call(methodInfo, result, Expression.Lambda<Func<string, World, Player, string>>(textPrintName, inputString, inputWorld, inputPlayer))),
Expression.Lambda<Action<string, World, Player>>(killPlayer, inputString, inputWorld, inputPlayer),
innerTest);
Expression failure =
Expression.Assign(result, Expression.Call(methodInfo, result, Expression.Lambda<Func<string, World, Player, string>>(textPrintNotExist, inputString, inputWorld, inputPlayer)));
Expression outerTest =
Expression.Condition(
Expression.Invoke(Expression.Lambda<Func<string, World, Player, bool>>(testExists, inputString, inputWorld, inputPlayer)),
success,
failure);
Expression finalExpression =
Expression.Block(
Expression.Assign(result, Expression.Call(methodInfo, result, Expression.Lambda<Func<string, World, Player, string>>(textPrintStarting, inputString, inputWorld, inputPlayer))),
outerTest);
return Expression.Lambda<Func<string, World, Player, string>>(
Expression.Block(new[] { result },
finalExpression)).Compile()(text, world, player);
}
问题在于 Condition 语句引发错误,因为它无法从 Func 转换为 bool。
我也不确定参数是否被传入(因为我无法调试)
【问题讨论】:
-
您能详细说明一下吗?你在你的问题中所说的对我来说没有意义。你有什么(请举一个具体的例子)?你想用它做什么(再次,请具体说明)?你有哪些表达方式,你打算如何使用它们?
-
我在当前的问题实现中添加了更多细节
-
有什么特别的原因为什么你想要所有这些作为一个表达式?我认为根本不需要它。如果您想要表达式的示例并查看它是否实际可行,您可以将函数存储为 lambda 表达式。
-
每个都是表达式的原因是我可以将它们构建成表达式树。
-
最终程序将有很大一部分代码作为动态生成的表达式树。这些树将由可更改的配置文件构建而成,可能很简单(如上面的那个)或更复杂。我希望将它作为二叉树,除了节点必须做的不仅仅是测试(必须是单独的表达式)并且表达式表示返回布尔值而不是仅仅返回布尔值的函数。 if 语句也可能要复杂得多。
标签: c# c#-4.0 expression-trees