我在业余时间制作一个对象验证框架,以学习一些东西,并可能将它用于一些学校项目。
我有我的通用Rule类,它看起来像这样:
class Rule<T> { string propertyName; Func<T, bool> ruleLambda; bool IsBroken(T value) { return ruleLambda(value); } }将被验证的对象看起来有点像这样:
class Example { List<Rule<?>> MyRules; // can take all types of rules List<Rule<T>> Validate<T>(string propertyName, T value) { List<Rule<T>> brokenRules = new List<Rule<T>>(); foreach (Rule rule in MyRules.Where(r => r.propertyName == propertyName)) { if (rule.IsBroken(value)) brokenRules.Add(rule); } return brokenRules; } }其中T value参数是Example类的一个属性的值,可以是任何类型。
只要设置了属性,就会调用Validate<T>方法。
问题在于班级的规则列表。 特别是上面的List<Rule<?>>行。 我想在同一个列表中存储给定类的所有规则。
唉,C#没有像Java那样的通用类型的通配符。
我该怎么做?
使用对象而不是T的非泛型接口或基类可以工作,但是如何调用泛型规则的IsBroken方法而不是非泛型方法呢?
I'm making an object validation framework in my spare time to learn a few things and maybe use it for some school projects.
I have my generic Rule class, which looks something like this :
class Rule<T> { string propertyName; Func<T, bool> ruleLambda; bool IsBroken(T value) { return ruleLambda(value); } }An object that would be validated would look a bit like this :
class Example { List<Rule<?>> MyRules; // can take all types of rules List<Rule<T>> Validate<T>(string propertyName, T value) { List<Rule<T>> brokenRules = new List<Rule<T>>(); foreach (Rule rule in MyRules.Where(r => r.propertyName == propertyName)) { if (rule.IsBroken(value)) brokenRules.Add(rule); } return brokenRules; } }Where the T value argument would be the value of one of the Example class's properties, which can be of any type.
The Validate<T> method is called whenever a property is set.
The problem lies with the class's list of rules. Specifically the List<Rule<?>> line above. I want to store all the rules for a given class in the same list.
Alas, C# doesn't have a wildcard for generic types like in Java.
How should I do this?
A non-generic interface or base class utilizing objects instead of T could work, but how would I call the generic Rule's IsBroken method and not the non-generic one?
最满意答案
我将您的规则作为object存储在Example类中,并使用Enumerable.OfType<T>来查找给定类型的匹配规则:
class Example { private List<object> rules; List<Rule<T>> Validate<T>(string propertyName, T value) { return this.rules.OfType<Rule<T>>() .Where(r => r.PropertyName == propertyName && r.IsBroken(value)) .ToList(); } }I've tried a few things and I've found something that works pretty well for my needs. I have Rule<T> inherit from a base abstract rule class, with a generic IsBroken method:
abstract class Rule { string propertyName; Func<object, bool> objectRule; bool IsBroken<T>(T value) { Rule<T> rule = this as Rule<T>; if (rule == null) return objectRule(value); return rule.IsBroken(value); } }As you can see, I try to convert the base class to its generic counterpart using the generic type parameter in the IsBroken method.
Also, when creating a Rule<T> instance, I send a Func<object, bool> to its base class protected constructor:
public Rule(string propertyName, Func<T, bool> ruleLambda) : base(propertyName, ConvertToObjectFunc(ruleLambda)) { }With the conversion method looking like this:
static Func<object, bool> ConvertToObjectFunc(Func<T, bool> func) { return new Func<object, bool>(o => func((T)o)); }However, if it can't cast o to type T, it crashes. So I wrote this... thing:
static Func<object, bool> ConvertToObjectFunc(Func<T, bool> func) { return new Func<object, bool> ( o => { try { T obj = (T)o; return func(obj); } catch { return true; } // rule is broken by default } ); }It's pretty ugly, but it works. Hope this can help anybody else.
更多推荐
发布评论