Lambda Func <> и свободно - PullRequest
       24

Lambda Func <> и свободно

7 голосов
/ 21 августа 2009

В настоящее время существует множество реализаций 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
     }
}

Мне удалось получить часть всей этой работы, используя такой метод на моем Валидаторе, как этот ...

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
}

Затем я могу создать методы Extension, которые расширяют IValidator для целей CannotBeNull и CannotBeEmpty.

Так что, похоже, у меня есть первая половина и вторая половина проблемы, но я не уверен, как их собрать.

В поисках значимого объяснения ... Я бы хотел "получить его". :)

Ответы [ 2 ]

5 голосов
/ 21 августа 2009

Ключом к свободным интерфейсам является то, что такие методы, как 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 голос
/ 21 августа 2009

Джриста ответ правильный. Просто для другого подхода вот как я это сделал.

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