Какой самый гладкий и привлекательный синтаксис вы нашли для подтверждения правильности параметров в c #? - PullRequest
29 голосов
/ 21 марта 2009

Обычная проблема любого языка - утверждать, что параметры, отправленные в метод, соответствуют вашим требованиям, а если нет, отправлять красивые, информативные сообщения об ошибках. Этот вид кода повторяется снова и снова, и мы часто пытаемся создать помощников для него. Однако в C # кажется, что эти помощники вынуждены иметь дело с некоторым дублированием, навязанным нам языком и компилятором. Чтобы показать, что я имею в виду, позвольте мне представить некоторый сырой код без помощников, а затем один возможный помощник. Затем я укажу на дублирование в помощнике и точно сформулирую мой вопрос.

Сначала код без всяких помощников:

public void SomeMethod(string firstName, string lastName, int age)
{
     if(firstName == null)
     {
          throw new WhateverException("The value for firstName cannot be null.");
     }

     if(lastName == null)
     {
          throw new WhateverException("The value for lastName cannot be null.");
     }

     // Same kind of code for age, making sure it is a reasonable range (< 150, for example).
     // You get the idea
}

}

Теперь код с разумной попыткой помощника:

public void SomeMethod(string firstName, string lastName, int age)
{
      Helper.Validate( x=> x !=null, "firstName", firstName);
      Helper.Validate( x=> x!= null, "lastName", lastName);
}

Основной вопрос заключается в следующем: Обратите внимание, как код должен передавать значение параметра и имя параметра ("firstName" и имя). Это сообщение об ошибке может сказать: «Бла-бла-бла, значение параметра firstName ». Вы нашли способ обойти это, используя отражение или что-то еще? Или способ сделать его менее болезненным?

И вообще, нашли ли вы другие способы упростить эту задачу проверки параметров при одновременном уменьшении дублирования кода?

РЕДАКТИРОВАТЬ: Я читал людей, говорящих об использовании свойства Parameters, но так и не нашел способ обойти это дублирование. Кому-нибудь повезет с этим?

Спасибо!

Ответы [ 14 ]

15 голосов
/ 21 марта 2009

Вы должны проверить Кодовые контракты ; они делают в точности то, что вы просите. Пример:

[Pure]
public static double GetDistance(Point p1, Point p2)
{
    CodeContract.RequiresAlways(p1 != null);
    CodeContract.RequiresAlways(p2 != null); 
    // ...
}
11 голосов
/ 22 марта 2009

Ух ты, я нашел здесь кое-что действительно интересное. Крис выше дал ссылку на другой вопрос переполнения стека. Один из ответов там указывал на сообщение в блоге, в котором описано, как получить код, подобный этому:

public static void Copy<T>(T[] dst, long dstOffset, T[] src, long srcOffset, long length)
{
    Validate.Begin()
            .IsNotNull(dst, “dst”)
            .IsNotNull(src, “src”)
            .Check()
            .IsPositive(length)
            .IsIndexInRange(dst, dstOffset, “dstOffset”)
            .IsIndexInRange(dst, dstOffset + length, “dstOffset + length”)
            .IsIndexInRange(src, srcOffset, “srcOffset”)
            .IsIndexInRange(src, srcOffset + length, “srcOffset + length”)
            .Check();

    for (int di = dstOffset; di < dstOffset + length; ++di)
        dst[di] = src[di - dstOffset + srcOffset];
}

Я не уверен, что это лучший ответ , но это, безусловно, интересно. Вот сообщение в блоге , от Рика Брюстера.

7 голосов
/ 21 марта 2009

Я решил эту проблему несколько недель назад, подумав, что странно, что библиотекам тестирования, по-видимому, нужен миллион различных версий Assert, чтобы сделать их сообщения описательными.

Вот мое решение .

Краткое резюме - учитывая этот бит кода:

int x = 3;
string t = "hi";
Assert(() => 5*x + (2 / t.Length) < 99);

Моя функция Assert может распечатать следующую сводку того, что ей передано:

(((5 * x) + (2 / t.Length)) < 99) == True where
{
  ((5 * x) + (2 / t.Length)) == 16 where
  {
    (5 * x) == 15 where
    {
      x == 3
    }
    (2 / t.Length) == 1 where
    {
      t.Length == 2 where
      {
        t == "hi"
      }
    }
  }
}

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

6 голосов
/ 22 марта 2009

Хорошо, ребята, это снова я, и я нашел что-то еще, что удивительно и восхитительно. Это еще одно сообщение в блоге, на которое ссылается другой SO вопрос, упомянутый Крисом выше.

Подход этого парня позволяет вам написать:

public class WebServer
    {
        public void BootstrapServer( int port, string rootDirectory, string serverName )
        {
            Guard.IsNotNull( () => rootDirectory );
            Guard.IsNotNull( () => serverName );

            // Bootstrap the server
        }
    }

Обратите внимание, что нет строки, содержащей «rootDirectory», и нет строки, содержащей «serverName» !! И все же его сообщения об ошибках могут содержать что-то вроде «Параметр rootDirectory не должен быть нулевым».

Это именно то, что я хотел и больше, чем я надеялся. Вот ссылка на пост в блоге парня.

А реализация довольно проста:

public static class Guard
    {
        public static void IsNotNull<T>(Expression<Func<T>> expr)
        {
            // expression value != default of T
            if (!expr.Compile()().Equals(default(T)))
                return;

            var param = (MemberExpression) expr.Body;
            throw new ArgumentNullException(param.Member.Name);
        }
    }

Обратите внимание, что здесь используется «статическое отражение», поэтому в тесном цикле или что-то в этом роде вы можете использовать подход Рика Брюстера, описанный выше.

Как только я опубликую это, я собираюсь проголосовать за Криса и ответ на другой ТАК вопрос. Это хороший материал !!!

6 голосов
/ 21 марта 2009

Это может быть несколько полезно:

Дизайн по контракту / C # 4.0 / исключение ArgumentNullException

5 голосов
/ 21 марта 2009

Использование моей библиотеки Помощник Троицы :

public void SomeMethod(string firstName, string lastName, int age)
{
    firstName.AssertNotNull("firstName");
    lastName.AssertNotNull("lastName");

    ...
}

Также поддерживает утверждение, что параметры перечисления правильны, коллекции и их содержимое не являются null, строковые параметры не являются пустыми и так далее. См. Пользовательскую документацию здесь для подробных примеров.

4 голосов
/ 05 июля 2009

Вот мой ответ на проблему. Я называю это " Страж Когтей ". Он использует синтаксический анализатор IL из общих библиотек Lokad, но имеет более простой подход к формулировке фактических охранных предложений:

string test = null;
Claws.NotNull(() => test);

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

Поскольку он использует реальные лямбда-выражения в качестве входных данных и использует синтаксический анализатор IL только для генерации исключения в случае нарушения, он должен работать лучше на «счастливом пути», чем схемы, основанные на выражениях, где-либо еще в этих ответах.

Ссылки не работают, вот URL: http://github.com/littlebits/guard_claws/

2 голосов
/ 18 июня 2009

Общие библиотеки Lokad также имеют реализацию, основанную на синтаксическом анализе IL, которая позволяет избежать дублирования имени параметра в строке.

Например:

Enforce.Arguments(() => controller, () => viewManager,() => workspace);

Будет выдано исключение с соответствующим именем параметра, если какой-либо из перечисленных аргументов будет нулевым. Он также имеет очень аккуратную реализацию правил на основе политики.

1010 *, например *

Enforce.Argument(() => username,    StringIs.Limited(3, 64), StringIs.ValidEmail);
1 голос
/ 21 марта 2009

Я бы предпочел просто оценить условие и передать результат, а не передать выражение для оценки и параметр, по которому его нужно оценить. Кроме того, я предпочитаю иметь возможность настроить все сообщение. Обратите внимание, что это просто предпочтения - я не говорю, что ваш пример неправильный - но в некоторых случаях это очень полезно.

Helper.Validate( firstName != null || !string.IsNullOrEmpty(directoryID),
                 "The value for firstName cannot be null if a directory ID is not supplied." );
0 голосов
/ 23 марта 2009

В отличие от этого, этот пост Miško Hevery в блоге тестирования Google утверждает, что такого рода проверка параметров не всегда может быть полезной , В результате обсуждения в комментариях также возникают некоторые интересные моменты.

...