2010-01-09 2 views
3

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

Я скачал .NET source code более внимательно взглянуть на биты и болты (с комментариями разработчиков, то, что отражатель не дает нам :-P)

Во всяком случае, во многих .NET классы Я натолкнулся на следующий образец:

class SomeType 
{ 
    // ... 
    SomeClass m_Field; 
    // ... 
    SomeClass SomeMethod() 
    { 
    SomeClass localField = m_Field; 
    if (localField == null) 
    { 
     localField = new SomeClass(); 
     m_Field = localField; 
    } 
    return localField; 
    } 
} 

Это заставило меня задаться вопросом, в чем преимущество использования такого шаблона?

Насколько я знаю, рисунок выше хуже, производительность мудрым, чем ниже:

class SomeType 
{ 
    // ... 
    SomeClass m_Field; 
    // ... 
    SomeClass SomeMethod() 
    { 
    if (m_Field == null) 
    { 
     m_Field = new SomeClass(); 
    } 
    return m_Field; 
    } 
} 

Или я что-то пропустил?

+1

Можете ли вы дать нам конкретный пример? Какой тип вы видели для SomeClass? Это какая-то коллекция? –

ответ

2

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

  1. безопасность Автора: алгоритму беззамочного, возможно, придется беспокоиться о это, но если вся синхронизация выполняется через блокировки, это не должно быть проблемой.

  2. Производительность: Это может быть немного быстрее в некоторых случаях, но я честно сомневаюсь, что это будет иметь значение в большинстве случаев.

  3. Исключение: Часто вам нужно быть осторожным, чтобы внести промежуточные изменения в местные жители, а затем публиковать результаты только по полям после завершения операций без привлечения исключения. Это действует как механизм транзакции, так как никто не увидит объект только с половиной установленных полей.

+1

Переполнение стека просто бросило мне вызов, чтобы я не был человеком. Я здесь новый и веселяюсь сегодня. –

+0

Вам не нравится фото, которое они показывают парням в костюмах роботов! –

0

Это может быть просто подсказкой для компилятора, что поле должно считываться в регистр, а не повторно обращаться к нему в памяти. Существует мало причин, почему первая версия должна быть хуже по производительности, чем вторая, которую вы указали. Это почти тот же код, который компилятор будет генерировать в любом случае. Прочитайте поле объекта в регистре, проверьте, является ли оно нулевым, измените его по желанию, затем запишите его обратно в поле объекта в памяти.

2

Такой подход может помочь защитить вас от ситуаций, когда вы вызываете SomeMethod из одного потока, но после того, как вы проверили m_Field на null, управление передается другому потоку, который устанавливает его в null. Затем элемент управления возвращается в первый поток, но он все еще считает, что m_Field! = Null, что, вероятно, приведет к NullReferenceException

Насколько я помню, в главе «CLR via C#» Рихтера есть несколько слов о главе Мероприятия.

0

MS .NET JIT делает register allocation. В таком простом случае эта временная переменная должна содержаться в регистре, а не в стеке. Созданный код байта x86 должен запускаться быстрее, чем если бы член класса читался дважды (один раз, чтобы проверить значение null, один раз для возврата) для случая, отличного от нуля, и быстрее для нулевого случая.

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

Но если используется переменная локальная компилятор предполагает, что не надо писать изменения в локальной переменной (или даже сохранить его в стеке), потому что доступ к местным жителям другой поток не является чем-то, что C# позволяет.

Смежные вопросы