Создание нулевых объектов с помощью оператора нуль-слияния

12

Рассмотрим следующий типичный сценарий:

if(myObject == null) {
    myObject = new myClass();
}

Мне интересно, что думают о следующей замене с использованием оператора нуль-слияния:

myObject = myObject ?? new myClass();

Я не уверен, должен ли я использовать вторую форму. Это кажется хорошим сокращением, но myObject = myObjectконструкция в начале кажется, что это может быть немного запахом кода.

Это разумная вещь, или есть лучшая стенография, которую мне не хватает? Или, может быть, «Это три линии, преодолеть это!»?

Изменить: Как уже упоминалось, возможно, назвать это типичным сценарием является чем-то завышенным. Обычно я сталкиваюсь с такой ситуацией, когда извлекаю сущность из базы данных, у которой есть свойство дочернего ссылочного типа, которое может быть заполнено или не заполнено:

myClass myObject = myClassService.getById(id);
myObject.myChildObject = myObject.myChildObject ?? new myChildClass();
grin0048
источник
15
Новички полагают, что такие вещи являются «хакерскими», более опытные разработчики просто называют это «идиоматическими».
Док Браун
Если объект выглядит как объект значения («имеет семантику значения», идиоматически), MyClassследует предоставить public static readonlyэлемент Emptyзначения его типа. Смотрите String.Emptyна MSDN .
rwong
5
Разве нет ??=оператора?
авив
1
@aviv Я бы хотел, чтобы это было!
Лорен Печтел

Ответы:

16

Я использую оператор слияния null все время. Мне нравится лаконичность этого.

Я считаю, что этот оператор по своей природе похож на троичный оператор (A? B: C). Требуется немного практики, прежде чем читать это станет второй натурой, но как только вы привыкнете, я чувствую, что читаемость улучшается по сравнению с рукописными версиями.

Кроме того, ситуация, которую вы описываете, является только одним сценарием, где оператор полезен. Также удобно заменять конструкции следующим образом:

if (value != null)
{
    return value;
}
else
{ 
    return otherValue;
}

или

return value != null ? value : otherValue;

с

return value ?? otherValue;
17 из 26
источник
Определенно согласен в целом об использовании этого оператора. Когда я вижу ссылки на его использование, это обычно что-то вроде myObject = myObject2 ?? myObject3. Что меня особенно интересует, так это использование оператора для установки самого объекта, если он не равен нулю.
grin0048
2
Я думаю, что те же самые рассуждения применимы в любом случае. Это более краткий способ выразить ту же логику.
17 из 26
Я помню, как смотрел на IL для каждой из этих трех форм один раз. Первый и второй были идентичны, но третий был оптимизирован таким образом, что можно было предположить, что nullэто сравнение.
Джесси С. Слайсер
2

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

Оператор ?? operatorназывается null-coalescing и используется для определения значения по умолчанию для типов значений NULL или ссылочных типов. Возвращает левый операнд, если операнд не равен нулю; в противном случае он возвращает правильный операнд.

myObject = myObject ?? new myObject(); - instantiate default value of object

Более подробная информация и пример кода об операторе слияния - статья MSDN .

Если вы проверите для more than nullableусловия, в качестве альтернативы вы можете использовать троичный оператор.

Тернарный оператор

Вы тоже можете посмотреть ?: Оператор . Это называется тернарным или условным оператором. Условный оператор (? :) возвращает одно из двух значений в зависимости от значения логического выражения.

Обнуляемый тип может содержать значение или может быть неопределенным. ?? Оператор определяет значение по умолчанию, которое будет возвращено, когда тип, который можно обнулять, назначается типу, не обнуляемому. Если вы попытаетесь назначить тип значения, допускающий значение NULL, для типа, не допускающего значения NULL, без использования ?? оператор, вы будете генерировать ошибку во время компиляции. Если вы используете приведение, а тип значения Nullable в настоящее время не определен, будет выдано исключение InvalidOperationException.

Пример кода из MSDN -?: Оператор (C # Reference) :

int? input = Convert.ToInt32(Console.ReadLine());
string classify;

// ?: conditional operator.
classify = (input.HasValue) ? ((input < 0) ? "negative" : "positive") : "undefined";
Юсубы
источник
Итак, взяв пример из вопроса и применив условный оператор, мы получим что-то вроде myObject = (myObject == null) ? new myClass() : myObject. Так что, если я не пропущу лучшее использование условного оператора, у него, похоже, есть та же «проблема» с установкой объекта себе (если он не нулевой), как с оператором слияния с нулем - и он менее лаконичен.
grin0048
Если вы проверяете более одного условия (не ноль и больше нуля) - тогда это сделает условный оператор. Тем не менее, просто нулевая проверка будет работать для ?? оператор.
Юсубов
2

Я не думаю, что сценарий является (или, по крайней мере, должен быть) типичным.

Если вы хотите, чтобы значение некоторого поля по умолчанию не было null, установите его в инициализаторе поля:

myClass myObject = new myClass();

Или, если инициализация более сложная, установите ее в конструкторе.

Если вы хотите создавать myClassтолько тогда, когда это действительно нужно (например, потому что создание занимает много времени), тогда вы можете использовать Lazy<T>:

Lazy<myClass> myObject = new Lazy<myClass>();

(Это вызывает конструктор по умолчанию. Если инициализация более усложняется, передать лямбда , что создает myClassв Lazy<T>конструкторе.)

Чтобы получить доступ к значению, используйте myObject.Value, который вызовет инициализацию, если вы обращаетесь в первый раз myObject.Value.

svick
источник
Ленивое создание IMO полезно только в том случае, если вам гарантированно не нужен результирующий объект, только когда я использую отложенное создание, это когда у меня получен результат, полученный при кассовых операциях, который я сбрасываю, когда что-то меняется, и я знаю, что мне понадобится тот же самый результат. и пересчет дорогой. в противном случае я просто не хочу возиться с нулевой проверкой
чокнутый урод
@ratchetfreak Да, именно поэтому я сначала предложил инициализатор поля.
svick
Есть и другие случаи. На что я сейчас обращаю внимание: первый проход устанавливает исключения. Второй проход устанавливает значения по умолчанию для всего остального. ?? = разрезал бы линию пополам.
Лорен Печтел
1

Особенно мне нравится использовать ??в сочетании с дополнительными параметрами метода. Я ограничиваю потребность в перегрузках и гарантирую, что вещь имеет ценность.

public void DoSomething (MyClass thing1 = null) {
    thing1 = thing1 ?? new MyClass();
}
radarbob
источник