Ich habe ein übermäßig kompliziertes System zur Erstellung von binären Ausdrucksbäumen Es nimmt eine Zeichenkette und ein Paar von Objekten (Player und World) auf
Jeder Knoten im Baum stellt eine externe Funktion dar, die eine Zeichenkette, einen Spieler und eine Welt annimmt und entweder ein bool (für Tests), eine Zeichenkette (für Ausgaben) oder ein void (für Aktionen) zurückgibt.
Mein Problem ist ein dreifaches: Erstens muss ich etwas verwenden wie Expression.Condition
o Expression.IfThenElse
wobei der Testausdruck die folgende Form hat Expression<func<string, Player, World, bool>>
statt Expresson<bool>
(als Expression.And
ausgeben würde)
Zweitens muss ich sicher sein, dass der Speicherverweis für Player und Welt die gleiche durchgehend bleiben - so dass, wenn einer der Knoten im Baum etwas in Player aktualisiert, dann wird es noch am nächsten Knoten aktualisiert werden.
Schließlich muss ich alle Zeichenfolgen aneinander anhängen.
Wenn ich den Baum hart kodieren könnte, würde er in etwa so aussehen:
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;
}
}
Um dies weiter auszuführen: Ich habe eine Instanz von SomeClass mit all ihren Test/Assign/String-Methoden. Dann erstelle ich eine Liste von Expression<func<string[], World, Player, bool>>
, Expression<Action<string[], World, Player>>
y Expression<func<string[], World, Player, string>>
und fangen an, sie zu einem Ausdrucksbaum zusammenzufügen. Die tatsächliche Reihenfolge der was geht, wo ich mit verlassen mich mit (zum Beispiel) behandelt haben:
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);
}
Das Problem liegt in der Condition
Anweisungen, die einen Fehler auslösen, weil sie nicht von Func nach bool konvertieren können. Ich bin mir auch nicht sicher, ob die Parameter übergeben werden (da ich nicht in der Lage war, durch zu debuggen)