Создание объектов с нулевыми объектами с помощью оператор
Рассмотрим следующий типичный сценарий:
if(anObject == null)
{
anObject = new AClass();
}
Мне интересно, что думают о следующей замене, используя Оператор:
anObject = anObject ?? new AClass();
Я не уверен, должен ли я использовать вторую форму. Это похоже на красивую стенографию, но конструкция anObject = anObject в начале кажется, что это может быть немного кодовым запахом.
Это разумная вещь, или есть лучшая стенография, которую мне не хватает? Или, может быть, "Это три линии, справитесь с этим!"
Ответы
Ответ 1
Update:
Как указал О. Р. Карпер, вопрос заключается в том, является ли самоназвание кодовым запахом. Это 6 и два 3s в моей книге. Назначение вряд ли является дорогостоящей операцией, и вы делаете это в других областях в любом случае с большинством операторов математики.
Я склонен думать, что это не запах кода.
Я делаю это все время для ленивых объектов (небольшая вариация на вашем примере):
return _myClass ?? (_myClass = new MyClass());
Думаю, все в порядке. Как ни странно, я не склонен использовать Lazy<T>
... не уверен, почему, но опять же я не очень часто делаю ленивые вещи. Lazy<T>
более выразителен в своем намерении, как и в, вы можете прочитать, что элемент лениво создан, но технически он добавляет дополнительные object
служебные данные к существующему элементу. На самом деле я действительно не беспокоюсь об этом.
Что касается "преодоления", я думаю, что это, вероятно, относится к этой категории. Каждый из них в этом случае я думаю.
Ответ 2
Без присваивания результата выражения к той же переменной - например. a = b ?? new AClass();
- этот шаблон прекрасен и служит для чего-то вроде "резервного" нового экземпляра по умолчанию:
private MyClass anObject;
// ...
(anObject ?? new MyClass()).DoSomething();
В этом случае вновь созданный объект не сохраняется для последующего повторного использования.
При назначении одной и той же переменной, похоже, что вы инициализируете что-то лениво, и в этом случае использование Lazy<T>
будет тем больше выразительный способ:
private Lazy<MyClass> anObject = new Lazy<MyClass>();
// ...
anObject.Value.DoSomething();
Экземпляр будет создан последним при выполнении последней строки.
Ответ 3
void Main()
{
AClass anObject = null;
// option A
if (anObject == null)
{
anObject = new AClass();
}
// option B
anObject = anObject ? new AClass();
}
Сравнение опции A с оптимизированным IL-кодом с опцией B:
Option A Option B Description
IL_0000: ldnull IL_0000: ldnull // Push a null reference on the stack.
IL_0001: stloc.0 // anObject IL_0001: stloc.0 // anObject // Pop a value from stack into local variable 0.
IL_0002: ldloc.0 // anObject IL_0002: ldloc.0 // anObject // Load local variable 0 onto stack
IL_0003: dup // Duplicate the value on the top of the stack.
IL_0003: brtrue.s IL_000B IL_0004: brtrue.s IL_000C // Branch to target if value is non-zero
IL_0006: pop // Pop value from the stack
IL_0005: newobj AClass..ctor IL_0007: newobj AClass..ctor // Allocate an uninitialized object or value type and call ctor
IL_000A: stloc.0 // anObject IL_000C: stloc.0 // anObject // Pop a value from stack into local variable 0.
Как вы можете видеть, разница между двумя вариантами очень мала: вариант B (с использованием тернарного оператора) приводит к еще нескольким операциям в стеке.
Если ваш компьютер не работает по часовому механизму, вы не увидите никакой разницы в своем конечном коде, так как со многими вещами - делать то, что наиболее читаемо для вашего кода.
Ответ 4
Есть несколько шаблонов, которые я видел в моделях просмотра WPF (где я вижу этот шаблон, который используется в основном):
Метод 1 не-ленивый:
public class ViewModel
{
private readonly SomeValue _value;
public ViewModel()
{
_value = new SomeValue();
}
public SomeValue Value { get { return _value; } }
}
Метод 1 Ленивый:
public class ViewModel
{
private readonly Lazy<SomeValue> _value = new Lazy<SomeValue>(() => new SomeValue());
public SomeValue Value { get { return _value.Value; } }
}
Способ 2:
public class ViewModel
{
private SomeValue _value;
public SomeValue Value { get { return _value ?? (_value = new SomeValue()); } }
}
Основные различия между методом 1 и методом 2 - это когда и как создается объект. Метод 1 ленивый использует Lazy(T)
, который добавляет служебные данные к созданию. Это действительно необходимо для больших объектов, а модели WPF, как правило, не слишком тяжелые. Большие объекты могут использовать лени внутренне, чтобы разумно создавать объекты.
Метод1 ленив и метод 2 очень полезны, когда вид большой. Пользовательский интерфейс должен быть отзывчивым, поэтому отсрочка создания объекта обеспечивает лучший пользовательский интерфейс. Когда большие объекты связаны со сложным видом, это окажется необходимым.