Проверка параметра конструктора для null перед вызовом базы
Я обычно проверяю аргументы конструктора для нулевых значений следующим образом:
public class SomeClass(SomeArgument someArgument)
{
if(someArgument == null) throw new ArgumentNullException("someArgument");
}
Но скажите, что у меня есть класс, который наследуется от другого класса:
public abstract class TheBase
{
public TheBase(int id)
{
}
}
public class TheArgument
{
public int TheId { get; set; }
}
public class TheInheritor : TheBase
{
public TheInheritor(TheArgument theArgument) : base(theArgument.TheId)
{
}
}
И кто-то теперь создает экземпляр TheInheritor
следующим образом:
var theVar = new TheInheritor(null);
Я не могу придумать способ проверки null
перед тем, как base
будет вызываться (и выбрасывает NullReferenceException
). Короче говоря, конструктор TheBase
принимает экземпляр TheArgument
, я не вижу, как я мог бы провести эту проверку работоспособности. Но что, если TheArgument
связано только с TheInheritor
и существует много других классов, наследующих от TheBase
?
Любые рекомендации о том, как решить эту проблему?
Ответы
Ответ 1
Вы можете сделать это примерно так:
public TheInheritor(TheArgument theArgument)
: base(ConvertToId(theArgument))
{
}
private static int ConvertToId(TheArgument theArgument)
{
if (theArgument == null)
{
throw new ArgumentNullException("theArgument");
}
return theArgument.Id;
}
Или, в общем, что-то вроде этого:
public TheInheritor(TheArgument theArgument)
: base(Preconditions.CheckNotNull(theArgument).Id)
{
}
где Preconditions
- это класс утилиты в другом месте, например:
public static class Preconditions
{
public static T CheckNotNull<T>(T value) where T : class
{
if (value == null)
{
throw new ArgumentNullException();
}
return value;
}
}
(Это, конечно, теряет имя аргумента, но вы также можете передать это в случае необходимости.)
Ответ 2
В качестве альтернативы вы можете использовать выбор Func < > for id:
public class TheInheritor : TheBase
{
public TheInheritor(TheArgument theArgument, Func<TheArgument, int> idSelector)
: base(idSelector(theArgument))
{
...
}
}
или даже
public class TheInheritor<T> : TheBase where T : TheArgument
{
public TheInheritor(T theArgument, Func<T, int> idSelector)
: base(idSelector(theArgument))
{
...
}
}
Исключения будут падать сами по себе, а также заставит вызываемого решить, как указать объект Id
.
Ответ 3
Как правило, я буду беспокоиться только о параметрах, которые я использую в своем классе. Параметры, которые используются базовым классом I, просто пройдут прямо, и пусть этот класс беспокоится об этом.
Ответ 4
Вы можете вызвать базовый конструктор, как это (ради аргумента, принимающего -1, указывает недопустимое значение):
public class TheInheritor : TheBase
{
public TheInheritor(TheArgument theArgument) : base(theArgument == null ? -1 : theArgument.TheId)
{
if (theArgument == null)
{
throw new ArgumentNullException("theArgument");
}
}
}
Ответ 5
Начиная с С# 6.0 вы можете использовать оператор с нулевой связью в сочетании с нуль-условный оператор следующим образом:
public TheInheritor(TheArgument theArgument)
: base(theArgument?.TheId ?? throw new ArgumentNullException(nameof(theArgument)))
{
}