2013-09-06 3 views
10

Рассмотрим следующий типичный сценарий:Создание объектов с нулевыми объектами? Оператор

if(anObject == null) 
{ 
anObject = new AClass(); 
} 

Я интересно, что думает о следующей замены с использованием ?? оператор:

anObject = anObject ?? new AClass(); 

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

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

+2

Оба являются общими. –

+7

Если это будет запах кода, он не будет представлен в первую очередь. Вторая форма более кратка и должна быть предпочтительной. –

+0

@JeroenVannevel: вопрос: * не * ли оператор '??' является кодовым запахом, но является ли его использование в выражении, которое привязано к тому, что передается как один из его операндов, - это запах кода. –

ответ

12

Update:

Как О. Р. Mapper указал, вопрос в том, чтобы делать с ли себя назначение кода-запах. Это 6 и два 3s в моей книге. Назначение вряд ли является дорогостоящей операцией, и вы делаете это в других областях, в любом случае, с большинством операторов математики.

Я склонен думать, что это не запах кода.


Я делаю это все время для ленивых объектов (небольшое изменение на вашем примере):

return _myClass ?? (_myClass = new MyClass()); 

Я думаю, что это хорошо. Как ни странно, я не склонен использовать Lazy<T> ... не знаю, почему, но опять же я не очень часто делаю ленивые вещи. Lazy<T> более выразителен в своем намерении, так как вы можете прочитать, что элемент лениво создан, но технически он добавляет еще один object накладные расходы к существующему элементу. На самом деле, я действительно не беспокоюсь об этом.

Что касается «преодоления», я думаю, что это, вероятно, относится к этой категории. Кажется, каждый в своем случае в этом случае.

+1

Спасибо, очень хорошая идея. Я помету ответ через 12 минут. Теперь я не могу поблагодарить. –

+1

Я не утверждаю, что какой-либо из них является кодовым запахом. В моем вышеприведенном комментарии я просто хотел бы указать, что было введено или нет * «это» * - * it *, являющееся '' 'оператором, - поскольку вопрос не задается вопросом, не является ли сам оператор это запах кода, но конкретный способ его использования показан в вопросе. –

+1

@ O.R.Mapper Извините, что я повторю, что я хотел сказать, вы подчеркнули, что вопрос касался самонаведения и был ли это запахом кода. Виноват. –

6

Не назначая результат выражения обратно той же переменной - например. a = b ?? new AClass(); - это модель прекрасно и служит для чего-то вроде «запасного варианта» нового экземпляра по умолчанию:

private MyClass anObject; 

// ... 

(anObject ?? new MyClass()).DoSomething(); 

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

При назначении той же переменной, это выглядит, как вы инициализации что-то лениво, в этом случае с помощью Lazy<T> будет более выразительным путь:

private Lazy<MyClass> anObject = new Lazy<MyClass>(); 

// ... 

anObject.Value.DoSomething(); 

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

+2

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

+1

@AdamHouldsworth: Вы правы, я улучшил свой выбор слов в ответе. –

+1

+1 для этой коррекции. Обычно я склонен избегать вопросов «лучшей практики», но я чувствовал, что этот был более безвредным, чем большинство. Вы склонны копать много хлопка, если вы идете против «нормы»: -P –

1
void Main() 
{ 
    AClass anObject = null; 

    // option A 
    if (anObject == null) 
    { 
     anObject = new AClass(); 
    } 
    // option B 
    anObject = anObject ? new AClass(); 
} 

Сравнивая вариант А с оптимизированным кодом 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 (с трехзначным оператором) в паре дополнительной операции стека.

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

+2

Так это просто сказать, что оператор coalesce производит более сжатый IL?Не уверен, как это только без объяснения ничего не отвечает, не говоря уже о том, как это удвоилось ... –

+1

-1, потому что сравнение неоптимизированного ИЛ в любом случае бессмысленно. – hvd

+0

Полученные очки ... улучшение ответа ... –

0

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