Подтвердить что ты не робот

Каков самый гладкий, наиболее привлекательный синтаксис, который вы нашли для утверждения правильности параметра в С#?

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

Во-первых, код без каких-либо помощников:

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). Это значит, что сообщение об ошибке может сказать: "Бла-бла-бла-значение для параметра firstName". Вы нашли какой-либо способ обойти это с помощью отражения или чего-нибудь еще? Или способ сделать это менее болезненным?

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

EDIT: я читал, что люди говорили об использовании свойства Parameters, но так и не нашли способ дублирования. Кому-нибудь повезло с этим?

Спасибо!

4b9b3361

Ответ 2

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

[Pure]
public static double GetDistance(Point p1, Point p2)
{
    CodeContract.RequiresAlways(p1 != null);
    CodeContract.RequiresAlways(p2 != null); 
    // ...
}

Ответ 3

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

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];
}

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

Ответ 4

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

Здесь мое решение.

Краткое резюме - с учетом этого бита кода:

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

Функция My 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"
      }
    }
  }
}

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

Ответ 5

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

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

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

            // Bootstrap the server
        }
    }

Обратите внимание: нет строки, содержащей "rootDirectory", и строки, содержащие "имя_сервера"!!. И все же его сообщения об ошибках могут сказать что-то вроде "Параметр 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

Использование моей библиотеки The Helper Trinity:

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

    ...
}

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

Ответ 7

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

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

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

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

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

Ответ 8

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

Например:

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

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

например.

Enforce.Argument(() => username,    StringIs.Limited(3, 64), StringIs.ValidEmail);

Ответ 9

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

Helper.Validate( firstName != null || !string.IsNullOrEmpty(directoryID),
                 "The value for firstName cannot be null if a directory ID is not supplied." );

Ответ 10

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

Среди тех.. System.ArgumentException, System.ArgumentNullException...

Ответ 11

Не знаю, переводит ли этот метод из C/С++ в С#, но я сделал это с помощью макросов:

    #define CHECK_NULL(x)  { (x) != NULL || \
           fprintf(stderr, "The value of %s in %s, line %d is null.\n", \
           #x, __FILENAME__, __LINE__); }
 

Ответ 13

Это не применяется повсеместно, но во многих случаях это может помочь:

Я предполагаю, что "SomeMethod" выполняет некоторую поведенческую операцию над данными "фамилия", "имя" и "возраст". Оцените свой текущий дизайн кода. Если три части данных плачут за класс, поместите их в класс. В этом классе вы также можете поместить свои чеки. Это освободит "SomeMethod" от проверки ввода.

Конечный результат будет примерно таким:

public void SomeMethod(Person person)
{
    person.CheckInvariants();
    // code here ...
}

Вызов будет примерно таким (если вы используете .NET 3.5):

SomeMethod(new Person { FirstName = "Joe", LastName = "White", Age = 12 });

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

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }

    public void CheckInvariants()
    {
        assertNotNull(FirstName, "first name");
        assertNotNull(LastName, "last name");
    }

    // here are your checks ...
    private void assertNotNull(string input, string hint)
    {
        if (input == null)
        {
            string message = string.Format("The given {0} is null.", hint);
            throw new ApplicationException(message);
        }
    }

Вместо синтаксического сахара .NET 3.5 вы также можете использовать аргументы конструктора для создания объекта Person.

Ответ 14

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