7

现在有很多 Fluent 实现可以与 Lambda 一起做非常简洁的事情。我想把我的大脑包裹在它周围,这样我就可以开始创造一些这样的东西,但我还没有找到我的大脑理解的解释。

考虑这个简单的人员验证器示例

public class PersonValidator : IValidator<Person>
{
     public PersonValidator()
     {
          AddRule(p => p.FirstName).CannotBeNull().CannotBeBlank();
          AddRule(p => p.LastName).CannotBeNull().CannotBeBlank();
     }

     public List<ValidationResult> Validate(Person p)
     {
         // pseudo...
         apply all rules specified in constructor, return results
     }
}

我已经设法在我的验证器上使用这样的方法来完成所有这些工作......

public ValidationResult<T,TProp> AddRule<T,TProp>(Func<T,TProp> property)
{
    ... not sure what to do here.  This method gives me the ability to use the lambda
    ... for specifying which properties i want to validate
}

然后,我可以创建扩展方法来扩展 IValidator 以实现CannotBeNull 和CannotBeEmpty。

所以似乎我有问题的前半部分和后半部分,但我不确定如何将它们组合在一起。

寻找一个有意义的解释......我想“得到它”。:)

4

2 回答 2

5

流畅接口的关键是CannotBeNull() 和CannotBeBlank() 等方法返回当前实例(即this)。如果您希望您的 AddRule 方法“流畅”,而不是返回 ValidationResult,您需要返回 IValidator 的当前实例。您的扩展方法还需要返回它们正在扩展的 IValidator 实例。

我认为您的确切实现可能需要更复杂一些,希望下面的示例能够提供一些见解。但是,相同的一般规则...返回“this”以创建流畅的界面:

interface IValidator<T>
{
    IValidatorRule<T, TProp> AddRule<TProp>(Func<T, TProp> property);
}

interface IValidatorRule<T>
{
    T instance { get; }
    string PropertyName { get; }

    ValidationResult Apply(T instance);
}

public static IValidatorAugmentorExtensions
{
    public static IValidatorRule<T> CannotBeNull(this IValidatorRule<T> rule)
    {
        // ...

        return rule;
    }

    public static IValidatorRule<T> CannotBeBlank(this IValidatorRule<T> rule)
    {
        // ...

        return rule;
    }
}

上面可以这样使用:

public class PersonValidator: IValidator<Person>
{
    public PersonValidator()
    {
        AddRule(p => p.FirstName).CannotBeNull().CannotBeEmpty();
        AddRule(p => p.LastName).CannotBeNull().CannotBeEmpty();
    }    

    public List<ValidationResult> Validate(Person p)
    {
        List<ValidationResult> results = new List<ValidationResult>();

        foreach (IValidatorRule<Person> rule in rules) // don't know where rules is, or what the AddRule method adds to...you'll need to figure that out
        {
            results = rule.Apply(p);
        }

        return results;
    }
}

虽然上面演示了如何创建一个流畅的界面,但我真的不知道在这种特殊情况下从长远来看它会给你带来什么。为了方便一个似乎只在内部用于具体验证器的流畅接口,您已经相当多地增加了代码的复杂性,而没有真正为验证器的使用者提供有用的、流畅的接口。我认为你会通过为需要执行验证的开发人员提供一个流畅的验证框架来收集更多价值,而不是提供一个用于创建具体验证器的流畅框架。

于 2009-08-21T03:58:31.263 回答
1

jrista 的回答是正确的。只是为了一种不同的方法,我是如何完成它的。

public class PersonValidator : IValidator<Person>
    {
        List<Func<Person,bool>> validationRules = new List<Func<Person,bool>>();

    public PersonValidator()
    {
        AddRule( p => IsNullOrEmpty(p.FirstName)).AddRule(p1 => CheckLength(p1.FirstName));
    }

    PersonValidator AddRule(Func<Person,bool> rule)
    {
        this.validationRules.Add(rule);
        return this;
    }

    private bool IsNullOrEmpty(String stringToCheck)
    {
        return String.IsNullOrEmpty(stringToCheck);
    }

    private bool CheckLength(String stringToCheck)
    {
        return (String.IsNullOrEmpty(stringToCheck) ? false : stringToCheck.Length < 3);
    }

    #region IValidator<Person> Members

    public bool Validate(Person obj)
    {
        return validationRules.Select(x => x(obj)).All(result => result == false);
    }

    #endregion
}



        Person test = new Person() { FirstName = null };
        Person test1 = new Person() { FirstName = "St" };
        Person valid = new Person() { FirstName = "John" };

        PersonValidator validator = new PersonValidator();
        Console.WriteLine("{0} {1} {2}", validator.Validate(test), validator.Validate(test1), validator.Validate(valid));
于 2009-08-21T04:09:07.263 回答