Проблемы при написании проверки параметров метода C #, которая поддерживает свободный интерфейс (цепочка вызовов) - PullRequest
2 голосов
/ 07 марта 2011

Я пытаюсь написать универсальную функциональность проверки параметров метода, которая может быть объединена в цепочку (свободный интерфейс), чтобы присоединять все больше проверок / проверок, таких как:

public void SomeMethod(User user, string description)
{
    ParameterHelper
        .Create(() => user)
        .RejectNull();
    ParameterHelper
        .Create(() => description)
        .RejectNull()
        .RejectEmptyString();

    // now this would be luxurious
    ParameterHelper
        .Create(() => new { user = user, desc = description })
        .RejectNull(o => o.user)
        .RejectNull(o => o.desc)
        .RejectEmptyString(o => o.desc);
}

Я хотел бы использовать этот вспомогательный класс для проверки параметров метода для определенных значений перед их использованием (большую часть времени будет проверяться null).

Текущее Положение

Я впервые начал писать статический вспомогательный класс без метода Create(), например:

public static class ParameterHelper
{
    public static void RejectNull(Expression<Func<T>> expr)
    {
        if (expr.Compile()().Equals(default(T)))
        {
            MemberExpression param = (MemberExpression)expr.Body;
            throw new ArgumentNullException(param.Member.Name);
        }
    }
}

Но это не позволяет цепочки. Вот почему я создал метод Create(), который будет возвращать что-то, что может быть использовано методами цепного расширения.

проблема

  1. Я бы хотел избежать нескольких вызовов Compile(), поэтому мой метод Create() должен возвращать Func<T>, а методы отклонения должны быть методами расширения Func<T>.
  2. Если мой Create() вернет Func<T>, у меня нет возможности прочитать имена параметров, которые должны быть предоставлены для различных исключений (используя MemberExpression).
  3. Если я верну Expression<Func<T>>, вместо этого мне придется вызывать Compile() в каждом Reject методе расширения.

Вопросы

  1. Существует ли библиотека C #, которая уже выполняет такую ​​цепочку?
  2. Если нет, что вы предлагаете, как это должно быть сделано? Любые примеры из Интернета будут тепло приветствоваться.

Дополнительные примечания

Я должен отметить, что сложный / длинный код вызова проверки не является вариантом здесь, потому что моя текущая проверка выполняется как:

if (user == null)
{
    throw new ArgumentNullException("user");
}

или

if (string.IsNullOrEmpty(description))
{
    throw new ArgumentNullException("description");
}

Который имеет два основных недостатка:

  1. Я повторяю одни и те же строки кода снова и снова
  2. используются магические строки

Таким образом, проверка должна выполняться с одним вкладышем на проверку, как описано выше в желаемом сценарии.

Ответы [ 2 ]

0 голосов
/ 18 апреля 2015

Роберт,

У меня есть библиотека, которая решает эту проблему.Он называется Bytes2you.Validation ( Project ).Это быстрая, расширяемая, интуитивно понятная и простая в использовании библиотека C #, предоставляющая свободные API для проверки аргументов.

Она ориентирована именно на проблему, которую вы хотите решить, но не использует выражения.Это так, потому что они намного медленнее, чем просто передача имени аргумента.Для такой библиотеки, которая предназначена для использования везде, производительность является одной из самых важных функций.

Например:

Guard.WhenArgument(stringArgument,"stringArgument").IsNullOrEmpty().IsEqual("xxx").Throw();  
// Which means - when stringArgument is null or empty OR is equal to "xxx" we will throw exception. If it is null, we will throw ArgumentNullException. If it is equal to "xxx", we will throw ArgumentException.
0 голосов
/ 07 марта 2011

Существует простой способ реализовать такой свободный интерфейс. Ваш метод 'ParameterHelper.Create' должен возвращать экземпляр некоторого класса (этот класс называется Requirements ниже). Этот экземпляр должен содержать выражение, которое было передано Create. Также этот класс должен иметь Require... экземпляр методов, которые будут проверять выражение и возвращать this. Requirements класс может быть закрытым классом внутри ParameterHelper. Я также хотел бы представить интерфейс для этой цепочки требований (этот интерфейс называется IRequirements ниже. Пример:

public static class ParameterHelper
{
    public static IRequirements Create<T>(Expression<Func<T>> expression)
    {
        return new Requirements{ Expression = expression };
    }

    private class Requirements<T> : IRequirements
    {
        public readonly Expression<Func<T>> Expression { get; set; }

        public IRequirements RejectNull()
        {
            if (Expression .Compile()().Equals(default(T)))
            {
                MemberExpression param = (MemberExpression)Expression.Body;
                throw new ArgumentNullException(param.Member.Name);
            }
            return this;
        }

        // other Require... methods implemented in the same way
    }
}

public interface IRequirements
{
    IRequirements RejectNull();
}

Этот подход позволит вам реализовать ваше решение luxurious - вам просто нужно добавить соответствующие параметры в методы Reject.... Также вам, вероятно, потребуется сделать IRequirements универсальный интерфейс.

...