Ein besseres Design für Dich wäre für die Regel der Prüfung selbst (oder auf einen beliebigen Wert) Dadurch dies mit Func Instanzen
anwenden finden Sie die größte Flexibilität erhalten, etwa so:
IEnumerable<Func<T,bool> tests; // defined somehow at runtime
foreach (var item in items)
{
foreach (var test in tests)
{
if (test(item))
{
//do work with item
}
}
}
dann würde Ihr spezieller Test so etwas für starke Typprüfung bei der Kompilierung sein:
public Func<T,bool> FooEqualsX<T,V>(V x)
{
return t => EqualityComparer<V>.Default.Equals(t.Foo, x);
}
für eine reflektierende Form
public Func<T,bool> MakeTest<T,V>(string name, string op, V value)
{
Func<T,V> getter;
var f = typeof(T).GetField(name);
if (f != null)
{
if (!typeof(V).IsAssignableFrom(f.FieldType))
throw new ArgumentException(name +" incompatible with "+ typeof(V));
getter= x => (V)f.GetValue(x);
}
else
{
var p = typeof(T).GetProperty(name);
if (p == null)
throw new ArgumentException("No "+ name +" on "+ typeof(T));
if (!typeof(V).IsAssignableFrom(p.PropertyType))
throw new ArgumentException(name +" incompatible with "+ typeof(V));
getter= x => (V)p.GetValue(x, null);
}
switch (op)
{
case "==":
return t => EqualityComparer<V>.Default.Equals(getter(t), value);
case "!=":
return t => !EqualityComparer<V>.Default.Equals(getter(t), value);
case ">":
return t => Comparer<V>.Default.Compare(getter(t), value) > 0;
// fill in the banks as you need to
default:
throw new ArgumentException("unrecognised operator '"+ op +"'");
}
}
Wenn Sie wollen, ohne bei der Kompilierung zu wissen, jeden wörtlichen wirklich introspektiv und zu handhaben sein, Sie die CSharpCodeProvider verwenden könnten eine Funktion unter der Annahme, so etwas wie zu kompilieren:
public static bool Check(T t)
{
// your code inserted here
}
Das ist natürlich ein massive Sicherheitsloch, wer Code für diese liefern kann, muss voll vertrauenswürdig sein. Hier ist eine etwas begrenzte Implementierung für Ihre spezifischen Bedürfnisse (kein Verstand überhaupt Kontrolle)
private Func<T,bool> Make<T>(string name, string op, string value)
{
var foo = new Microsoft.CSharp.CSharpCodeProvider()
.CompileAssemblyFromSource(
new CompilerParameters(),
new[] { "public class Foo { public static bool Eval("+
typeof(T).FullName +" t) { return t."+
name +" "+ op +" "+ value
+"; } }" }).CompiledAssembly.GetType("Foo");
return t => (bool)foo.InvokeMember("Eval",
BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod ,
null, null, new object[] { t });
}
// use like so:
var f = Make<string>("Length", ">", "2");
Für diese mit beliebigen Typen zu arbeiten, würden Sie ein bisschen mehr Reflexion zu tun haben, um die Zielanordnung zu finden für die Art zu referenzieren es in den Compiler-Parametern.
private bool Eval(object item, string name, string op, string value)
{
var foo = new Microsoft.CSharp.CSharpCodeProvider()
.CompileAssemblyFromSource(
new CompilerParameters(),
new[] { "public class Foo { public static bool Eval("+
item.GetType().FullName +" t) "+
"{ return t."+ name +" "+ op +" "+ value +"; } }"
}).CompiledAssembly.GetType("Foo");
return (bool)foo.InvokeMember("Eval",
BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod ,
null, null, new object[] { item });
}
Alle oben genannten Code einfach ein Proof of Concept ist, fehlt es an Verstand überprüft und ernsthafte Performance-Probleme hat.
Wenn Sie noch schicker sein möchten, können Sie Reflection.Emit mit DynamicMethod-Instanzen verwenden (indem Sie richtige Operatoren anstelle der Standardvergleichsinstanzen verwenden), aber dies würde eine komplexe Behandlung von Typen mit überschriebenen Operatoren erfordern.
Indem Sie Ihren Prüfcode hochgradig generisch machen, können Sie in Zukunft mehr Tests hinzufügen, als Sie benötigen. Im Wesentlichen isolieren Sie den Teil Ihres Codes, der nur eine Funktion betrifft, von t -> true/false aus dem Code, der diese Funktionen bereitstellt.
Dann scheint eine Lösung mit Delegierten ziemlich nah an dem zu sein, was Sie brauchen; es sei denn, der stringCode ist wirklich dynamisch (vom Benutzer bereitgestellter Inhalt im laufenden Betrieb). Dann brauchen Sie etwas durch die DLR (die in .NET 4.0 bevorsteht und mir nicht bekannt genug ist). –
Solange Ihr spezifischer Fall konstant bleibt, können Sie es erreichen. Wenn Sie die Möglichkeit hätten, * willkürliche * Logik anstelle der beschriebenen Form zu verwenden, hätten Sie Probleme. Im Wesentlichen solange die Menge aller Eingaben zu Ihrem "Code" im Voraus bekannt ist und es keine Nebeneffekte gibt außer der Rückgabe eines einzelnen Wertes (wieder wo der Typ zur Kompilierzeit bekannt ist (auch wenn nur als 'Objekt') dann ist das möglich Alles außerhalb dieses und du bist außerhalb dessen, was etwas wie 'eval' tun kann – ShuggyCoUk
Ah Ich habe gerade dein Update gesehen Nein, du kannst nicht die vollen Features von der Art von Dingen bekommen, die eval in etwas wie Javascript macht Der Aufwand, mehr und mehr Funktionalität zu bekommen, würde exponentiell härter werden, da Sie mehr und mehr Aspekte wollten, die funktionieren würden, wenn Sie den Code zur Kompilierungszeit einfach in die Klasse einfügen würden wurde zu einer Instanzvariable hochgestuft und der introspizierte Laufzeitstatus war der lexikalischen Struktur der Kompilierungszeit nicht mehr ausreichend ähnlich, um dem Konzept zu ermöglichen, weiter zu arbeiten. – ShuggyCoUk