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

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

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 очень полезны, когда вид большой. Пользовательский интерфейс должен быть отзывчивым, поэтому отсрочка создания объекта обеспечивает лучший пользовательский интерфейс. Когда большие объекты связаны со сложным видом, это окажется необходимым.