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

Каков наилучший способ продлить нулевую проверку?

Вы все это делаете:

public void Proc(object parameter)
{
    if (parameter == null)
        throw new ArgumentNullException("parameter");

    // Main code.
}

Джон Скит однажды упомянул, что он иногда использует расширение для выполнения этой проверки, поэтому вы можете сделать только:

parameter.ThrowIfNull("parameter");

Итак, я пришел с двумя реализациями этого расширения, и я не знаю, какой из них лучше.

Во-первых:

internal static void ThrowIfNull<T>(this T o, string paramName) where T : class
{
    if (o == null)
        throw new ArgumentNullException(paramName);
}

Во-вторых:

internal static void ThrowIfNull(this object o, string paramName)
{
    if (o == null)
        throw new ArgumentNullException(paramName);
}

Как вы думаете?

4b9b3361

Ответ 1

Я бы использовал internal static void ThrowIfNull<T>(this T o, string paramName) where T : class. Я не буду использовать internal static void ThrowIfNull(this object o, string paramName), потому что это может сделать бокс.

Ответ 2

Я склонен придерживаться вездесущего класса Guard для этого:

static class Guard
{
    public static void AgainstNulls(object parameter, string name = null)
    {
        if (parameter == null) 
            throw new ArgumentNullException(name ?? "guarded argument was null");

        Contract.EndContractBlock(); // If you use Code Contracts.
    }
}

Guard.AgainstNulls(parameter, "parameter");

И уклониться от расширения object, плюс невооруженным глазом, вызов метода на объект null кажется бессмысленным (хотя я знаю, что вполне допустимо иметь нулевые вызовы методов против методов расширения).

Как лучше всего, я бы не использовал ни одного. Оба они имеют бесконечную рекурсию. Я также не буду беспокоиться о сохранении параметра сообщения, сделав его необязательным. Ваше первое решение также не будет поддерживать типы Nullable<T>, поскольку ограничение class блокирует его.

Наш класс Guard также имеет вызов Contract.EndContractBlock() после него, когда мы решаем включить Code Contracts, поскольку он подходит для "if-then-throw" "которая требуется.

Это также идеальный кандидат на аспект PostSharp.

Ответ 3

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

public static void ThrowIfNull<T>(this T item) where T : class
{
    if (item == null)
        return;

    var param = typeof(T).GetProperties()[0];
    if (param.GetValue(item, null) == null)
        throw new ArgumentNullException(param.Name);
}

И назовите его:

public void Proc(object parameter)
{
    new { parameter }.ThrowIfNull(); //you have to call it this way.

    // Main code.
}

Снижение производительности тривиально (на моем посредственном компьютере он работал в 100000 раз чуть менее 25 мс), намного быстрее, чем обычный подход на основе выражения

ThrowIfNull(() => resource);

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

Вы также можете расширить это для свойств объектов.

new { myClass.MyProperty1 }.ThrowIfNull();

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

См. этот вопрос дополнительно: Разрешение имени параметра во время выполнения

Ответ 4

Как насчет использования деревьев выражений (из Журнал Visual Studio):

using System;
using System.Linq.Expressions;
namespace Validation
{
   public static class Validator
   {
     public static void ThrowIfNull(Expression<Func<object>> expression)
     {
       var body = expression.Body as MemberExpression;
       if( body == null)
       {
         throw new ArgumentException(
           "expected property or field expression.");
       }
       var compiled = expression.Compile();
       var value = compiled();
       if( value == null)
       {
         throw new ArgumentNullException(body.Member.Name);
       }
     }
     public static void ThrowIfNullOrEmpty(Expression<Func<String>> expression)  
     {
        var body = expression.Body as MemberExpression;
        if (body == null)
        {
          throw new ArgumentException(
            "expected property or field expression.");
        }
        var compiled = expression.Compile();
        var value = compiled();
        if (String.IsNullOrEmpty(value))
        {
          throw new ArgumentException(
            "String is null or empty", body.Member.Name);
        }
      }
   }

}

Используется следующим образом:

public void Proc(object parameter1, object parameter2, string string1)
{
    Validator.ThrowIfNull(() => parameter1);
    Validator.ThrowIfNull(() => parameter2);
    Validator.ThrowIfNullOrEmpty(() => string1);
    // Main code.
}

Ответ 5

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

internal static void ThrowIfNull(this object o, string paramName)
{
       if (o == null)
        throw new ArgumentNullException(paramName);
}