Будет ли компилятор C # оптимизировать переменную? - PullRequest
0 голосов
/ 01 января 2019

Это продолжение моего поста, Является ли это правильной реализацией параллельной наблюдаемой коллекции? .

В этом посте у меня есть собственный класс, который реализует универсальный параллельныйнаблюдаемый список, включая реализацию IEnumerable<T>.GetEnumerator().Это был оригинальный код:

public IEnumerator<T> GetEnumerator()
{
    var localSnapshot = _snapshot; //create local variable to protect enumerator, if class member (_snapshot) should be changed/replaced while iterating
    return ((IEnumerable<T>)localSnapshot).GetEnumerator();
}

С _snapshot, являющимся частным Array полем, которое перестраивается с помощью lock() всякий раз, когда изменяется фактическая внутренняя коллекция (List<T> _list).

Но теперь я думаю, что переменная localSnapshot вообще не требуется, код должен быть:

public IEnumerator<T> GetEnumerator()
{
    return ((IEnumerable<T>)_snapshot).GetEnumerator();
}

Поскольку localSnapshot просто назначается ссылка на тот же адрес, на который ссылается _snapshot,GetEnumerator не волнует (и не может сказать), какая переменная использовалась (и, конечно, для ее собственного использования создаст еще одну переменную, которая ссылается на тот же массив).

Если мое предположение выше верноИнтересно, если бы компилятор оптимизировал переменную?Тогда полученный код будет идентичен.Или, если нет: теоретически копирование ссылки может быть «вредным», потому что копия будет менее актуальной, чем могла бы быть (другой поток мог обновить _snapshot после того, как копия была сделана, но до GetEnumeratorназывается)?И является ли этот «побочный эффект» причиной, по которой компилятор не оптимизирует код - потому что оптимизации должны быть «без побочных эффектов»?

1 Ответ

0 голосов
/ 01 января 2019

Две версии кода будут давать один и тот же результат после компиляции.Как отмечает TheGeneral в комментариях, хороший способ убедиться в этом - проверить sharplab.io .

Обратите внимание, что это верно только в том случае, если вы компилируете в режиме Release.Если вы компилируете в режиме отладки, то компилятор предположит, что вам может понадобиться промежуточная переменная для целей отладки, и не оптимизирует ее.копия будет менее актуальной, чем могла бы быть (другой поток мог обновить _snapshot после того, как копия была сделана, но до вызова GetEnumerator)

Это было бы так, если бы у вас был какой-то кодвыполнение между var localSnapshot = _snapshot; и return ((IEnumerable<T>)localSnapshot).GetEnumerator().В такой ситуации оптимизация была бы невозможна.В противном случае, в обоих случаях вы читаете значение и напрямую его используете.Между двумя версиями кода нет разницы в «свежести».

...