Как добавить валидацию на Null на уровне конструктора?
abstract class Base
{
public Base(int i)
{
}
}
class Foo : Base
{
public Foo(string s) : base(s.Length)
{
}
}
С учётом того, что Base класс может быть в другой библиотеке, к изменению которой доступа нет. Пробовал null coalescing оператор, но ругается, что левая часть выражения должна быть ссылочного или nullable типа:
public Foo(string s) : base(s.Length ?? 0)
Буду рад любым подсказкам.
Ответы (2 шт):
Ну можно тернарный оператор использовать:
base(s == null ? 0 : s.Length)
Или так тоже можно
base(s?.Length ?? 0)
Хотя не очень понятно, какое вы поведение всё же хотите в случае null, но предположим, что такое.
Валидация обычно предполагает, что если аргумент неподходящий, то нужно бросать исключение или ещё как-то сигнализировать об ошибке, а тут вы пытаетесь приспособиться к ситуации. А может не надо этого делать?
Насколько я понимаю, вам нужно не подавить ошибку, а бросить исключение. Проблема в том, что вы не можете это сделать «внутри» тела конструктора, т. к. до начала работы тела конструктора вам нужно вызвать базовый конструктор, а проверка должна быть до вызова базового конструктора, то есть на этапе вычисления аргументов для базового конструктора.
Самый простой код, который это делает, видимо, такой:
public Foo(string s) : base((s ?? throw new ArgumentNullException(nameof(s))).Length)
{
}
Это выглядит нечитаемо (особенно если таких аргументов у вас много), поэтому, возможно, лучше вынести проверку в отдельную статическую функцию:
static string Checked(string s) => s ?? throw new ArgumentNullException("s");
public Foo(string s) : base(Checked(s).Length)
{
}
Ну или если хочется в старом стиле, то
static string Checked(string s)
{
if (s == null)
throw new ArgumentNullException("s");
return s;
}
Поскольку метод довольно общий, можно вынести его в отдельный helper и применять:
static class ValidationHelper
{
public static T EnsureNotNull(T? t, string name) where T : class =>
t ?? throw new ArgumentNullException(name);
}
и пользоваться как-то так:
using static ValidationHelper;
...
public Foo(string s) : base(EnsureNotNull(s, nameof(s)).Length)
{
}
Когда в будущем в C# реализуют-таки [CallerArgumentExpression], можно будет писать изящнее:
static class ValidationHelper
{
public static T EnsureNotNull(
T? t,
[CallerArgumentExpression(nameof(t))] string name = null)
where T : class =>
t ?? throw new ArgumentNullException(name);
}
...
using static ValidationHelper;
...
public Foo(string s) : base(EnsureNotNull(s).Length)
{
}