现在有许多Fluent实现可以与Lambdas一起完成非常整洁的工作。我想把我的大脑包裹起来,这样我就可以开始创造一些这样的东西了,但我还没有找到一个我的大脑能够理解的解释。
考虑一个Person Validator的简单例子
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
}
}
我已经设法使用我的Validator上的方法来完成所有这些工作的一部分...
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。
所以我似乎有问题的上半部分和后半部分,但我不确定如何将它们组合在一起。
寻找有意义的解释...我想“得到它”。 :)
答案 0 :(得分: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;
}
}
虽然上面演示了如何创建一个流畅的界面,但从长远来看,我并不知道它在这种情况下会给你带来什么。为了方便流畅的界面,似乎只在内部使用具体的验证器,您已经大大增加了代码的复杂性,而没有真正为验证器的使用者提供有用的,流畅的界面。我认为通过为需要执行验证的开发人员提供流畅的验证框架,而不是提供用于创建具体验证器的流畅框架,您可以收集更多价值。
答案 1 :(得分: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));