对您来说更好的设计是让您的规则应用测试本身(或任意值)
通过对 Func 实例执行此操作,您将获得最大的灵活性,如下所示:
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
}
}
}
那么您的特定测试将是这样的,用于在编译时进行强类型检查:
public Func<T,bool> FooEqualsX<T,V>(V x)
{
return t => EqualityComparer<V>.Default.Equals(t.Foo, x);
}
对于反射形式
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 +"'");
}
}
如果您想真正内省并在编译时不知道的情况下处理任何文字,您可以使用 CSharpCodeProvider 编译一个函数,假设如下:
public static bool Check(T t)
{
// your code inserted here
}
这当然是一个巨大的安全漏洞,因此必须完全信任能够为此提供代码的人。这是针对您的特定需求的有限实施(根本没有健全性检查)
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");
为此,要使用任意类型,您必须进行更多反射以找到该类型的目标程序集以在编译器参数中引用它。
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 });
}
上面所有的代码只是一个概念证明,它缺乏健全性检查并且存在严重的性能问题。
如果您想变得更漂亮,您可以将 Reflection.Emit 与 DynamicMethod 实例一起使用(使用正确的运算符而不是默认的比较器实例),但这需要对具有重写运算符的类型进行复杂的处理。
通过使您的检查代码高度通用,您可以在将来根据需要包含更多测试。从本质上将只关心函数的代码部分从 t -> true/false 与提供这些函数的代码隔离开来。