4

我在业余时间制作了一个对象验证框架来学习一些东西,并可能将它用于一些学校项目。

我有我的通用 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方法而不是非泛型方法?

4

3 回答 3

5

我会将您的规则存储objectExample类中,并用于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();
    }
}
于 2012-08-25T21:02:19.937 回答
3

如果我需要这样的东西,我会使用接口或非泛型基类。例如,您可以创建一个接口:

public interface IRule
{
  //non-generic properties & methods
}

public class Rule<T> : IRule
{
  //implementation
}

然后创建一个接口列表:

private List<IRule> MyRules;

如果你想让从接口到泛型的转换变得容易,你可以添加一个扩展方法:

public static Rule<T> ToGeneric<T>(this IRule rule)
{
  return rule as Rule<T>;
}
于 2012-08-25T20:42:57.347 回答
1

我已经尝试了一些东西,并且发现了一些非常适合我需要的东西。我Rule<T>从一个基本的抽象规则类继承了一个通用IsBroken方法:

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);
    }
}

如您所见,我尝试使用方法中的泛型类型参数将基类转换为其泛型对应物IsBroken

此外,在创建Rule<T>实例时,我将 a 发送Func<object, bool>到其基类受保护的构造函数:

public Rule(string propertyName, Func<T, bool> ruleLambda)
    : base(propertyName, ConvertToObjectFunc(ruleLambda))
{
}

转换方法如下所示:

static Func<object, bool> ConvertToObjectFunc(Func<T, bool> func)
{
    return new Func<object, bool>(o => func((T)o));
}

但是,如果它不能将 o 转换为类型 T,它就会崩溃。所以我写了这个……东西:

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
        }
    );
}

这很丑陋,但它有效。希望这可以帮助其他人。

于 2012-08-26T03:02:44.840 回答