Как избежать примитивной одержимости правильно с обработкой `default (T)`? - PullRequest
0 голосов
/ 22 сентября 2018

Я хочу избежать примитивной одержимости следующим struct.Для этого есть две цели:

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

Реализация:

public struct SomeId : IEquatable<SomeId>
{
    public static readonly SomeId Empty = new SomeId(String.Empty);

    private SomeId(string someId)
    {
        Value = someId;
    }

    public string Value { get; }

    public static bool operator ==(SomeId left, SomeId right) => left.Equals(right);
    public static bool operator !=(SomeId left, SomeId right) => !(left == right);
    public static implicit operator string(SomeId id) => id.Value;
    public override int GetHashCode() => Value.GetHashCode();
    public override bool Equals(object obj) => Value.Equals(obj);
    public bool Equals(SomeId other) => String.Equals(Value, other.Value, StringComparison.Ordinal);

    public static SomeId? Create(string value)
    {
        if (String.IsNullOrWhiteSpace(value))
        {
            return new SomeId(value);
        }

        return null;
    }
}

Довольно много кода, но все еще не идеально !Действительно, это решает основную проблему, заключающуюся в том, что я не буду передавать строки повсеместно, а буду давать методологически значимую подпись.

Тем не менее, я могу внедрить недопустимое значение в свое приложение, просто создав новый экземпляр с помощью default(SomeId)- поскольку Value имеет тип string, я получу SomeId в недопустимом состоянии с Value = null.

Какое будет лучшее решение здесь?Должен ли я вообще беспокоиться?

Конечно, я мог бы сделать, например, это:

private string _value;
public string Value => _value ?? String.Empty

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

1 Ответ

0 голосов
/ 22 сентября 2018

Должны ли вы заботиться?Да, я так думаю.Очень легко создавать структуры с нулевой инициализацией (default(SomeId), new SomeId(), new SomeId[n]), и ваша структура семантически недопустима в этом состоянии.

У вас есть несколько вариантов:

  • Нулевое слияние в геттере (предложенное вами решение).Вы правы, если поле пустое, это всегда приведет к выполнению еще нескольких инструкций.Вопрос в том, оказывают ли эти дополнительные инструкции (например, загрузить ноль, сравнить на равенство, загрузить статическое поле) ощутимое влияние на скорость выполнения?
  • Проверить null в геттере и установить для поля значение string.Empty если необходимо.Технически это геттер с побочными эффектами (даже если данные инкапсулированы), о которых некоторые люди твердо убеждены, но вы также можете назвать это ленивой инициализацией.
  • Объявить экземпляры по умолчанию недействительными, например ImmutableArray<T> делает.
...