Самостоятельное задание в С#

Я просматривал какой-то код, который я написал некоторое время назад, и понял, что сделал предположение о операторе присваивания в С#. Вот строка кода, о которой идет речь (она работает как ожидалось):

pointsChecked = pointsChecked ?? new List<Point>();

pointsChecked - это список, указанный как параметр для рекурсивной функции. Это параметр по умолчанию со значением по умолчанию null. То, что я хочу сделать, это инициализировать его один раз, а затем собрать коллекцию точек, которые я уже проверил, поэтому ее следует инициализировать только во время первой итерации.

Мое предположение заключалось в том, что С# защищен от самоопределения таким же образом, что С++ operator= должен обеспечивать защиту при перегрузке (т.е. if(this == &rightHandSide) return *this;). Однако я не смог найти какие-либо ресурсы, которые явно указывают, что это верно для С#.

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

Поиск в MSDN Я также обнаружил, что (перефразируя на основе моего понимания) значение с правой стороны копируется в значение с левой стороны и вернулся. Поэтому я снова не уверен, что плохо самонастраиваться.

Я знаю, что могу сделать следующее, чтобы быть более безопасным:

if(pointsChecked == null)
{
    pointsChecked = new List<Point>();
}

Но я предпочел бы понять, что на самом деле происходит с самоназначением.

Ответ 1

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

В С++ назначение настраивается, в С# это не так.

Безопасно назначить одну и ту же ссылку на объект, уже содержащий его:

object someRef = new object();
someRef = someRef; //always does nothing

Это безопасно, как назначение любого другого значения:

int someVal = 123;
someVal = someVal; //always does nothing

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

Самоназначение безопасно, потому что оно переводится в следующие приблизительные инструкции IL:

ldloc someRef
stloc someRef

Это четко обозначенная семантика. Сначала он загружает someRef в стек, а затем сохраняет все, что находится в стеке, на someRef.