В этом надуманном примере C# 8:
#nullable enable
class Fred<T>
{
T Value; // If T is a nullable type, Value can be null.
public Fred() { }
public void SetValue(T value) { Value = value; }
public T GetValue() { return Value; }
public string Describe() { return Value.ToString() ?? "oops"; }
}
class George
{
George()
{
Fred<George> fredGeorge = new Fred<George>();
George g = fredGeorge.GetValue();
Fred<float> fredFloat = new Fred<float>();
float f = fredFloat.GetValue();
}
}
У меня есть три цели проектирования:
- Компилятор должен предупредить меня, если я напишу какие-либо методы для Фреда, которые вслепую Предположим, что 'Value' никогда не будет нулевым
- Компилятор должен предупредить меня, если я напишу какие-либо методы за пределами Фреда (скажем, в George), которые слепо предполагают, что 'GetValue' никогда не вернет null. 1010 * Нет предупреждений компилятора (если я написал код, который слепо не предполагает, что значения не будут нулевыми)
Итак, эта первая версия неплохая, я получаю предупреждение от Фреда, которое описывает () может разыменовывать нулевую ссылку (удовлетворяет цели №1), но я также получаю предупреждение о том, что Value не инициализировано в конструкторе Фреда (нарушает цель №3) и Джордж компилируется без каких-либо предупреждений (нарушает цель №2). Если я сделаю это изменение:
public Fred() { Value = default; }
Джордж по-прежнему компилируется без предупреждений (нарушает цель № 2), и я получаю другое предупреждение в конструкторе Фреда о «Возможном присвоении нулевой ссылки» (нарушает цель № 3).
Я могу избавиться от возможного присвоения нулевой ссылки с помощью оператора, допускающего нулевые значения:
public Fred() { Value = default!; }
И теперь у Фреда есть только правильное предупреждение (возможное разыменование в Describe ()), но Джордж также компилируется без предупреждения (нарушает цель № 2).
Если я попытаюсь указать, что 'Value' может быть нулевым:
T? Value;
я получаю ошибку компилятора, которая "допускает значение NULL Параметр type должен быть известен как тип значения или ссылочный тип, не допускающий значения NULL, так что это бесполезно.
Если I go вернитесь к
T Value;
и добавьте «MaybeNull» attribute:
[return: MaybeNull]
public T GetValue() { return Value; }
Я получаю два предупреждения - одно в предупреждении Fred.Describe () о возможном разыменовании нуля (правильно) и одно в предупреждении Джорджа о том, что fredGeorge.GetValue () может быть нулевым (правильно). Нет предупреждения о том, что fredFloat.GetValue () имеет значение null (правильно).
Итак, после добавления кода для ожидания нулевых ссылок я получаю следующее:
class Fred<T>
{
T Value;
public Fred()
{
Value = default!;
}
public void SetValue(T value)
{
Value = value;
}
[return: MaybeNull]
public T GetValue()
{
return Value;
}
public string Describe()
{
return (Value == null) ? "null" : (Value.ToString() ?? "ToString is null");
}
}
class George
{
George()
{
Fred<George> fredGeorge = new Fred<George>();
George? g = fredGeorge.GetValue();
Fred<float> fredFloat = new Fred<float>();
float f = fredFloat.GetValue();
}
}
Это правильный шаблон для этой функции?