Ответ 1
Я бы использовал internal static void ThrowIfNull<T>(this T o, string paramName) where T : class
. Я не буду использовать internal static void ThrowIfNull(this object o, string paramName)
, потому что это может сделать бокс.
Вы все это делаете:
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);
}
Как вы думаете?
Я бы использовал internal static void ThrowIfNull<T>(this T o, string paramName) where T : class
. Я не буду использовать internal static void ThrowIfNull(this object o, string paramName)
, потому что это может сделать бокс.
Я склонен придерживаться вездесущего класса 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.
Я бы сделал так, чтобы избежать имен параметров жесткого кодирования. Завтра он может измениться, и у вас будет больше работы:
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();
Вы можете кэшировать значения свойств, чтобы повысить производительность, поскольку имена свойств не изменяются во время выполнения.
См. этот вопрос дополнительно: Разрешение имени параметра во время выполнения
Как насчет использования деревьев выражений (из Журнал 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.
}
Второй вариант выглядит более элегантным способом обработки. В этом случае вы можете установить ограничение на каждый управляемый объект.
internal static void ThrowIfNull(this object o, string paramName)
{
if (o == null)
throw new ArgumentNullException(paramName);
}