Что нужно сделать, чтобы изменения объекта внутри метода были видны снаружи в C#
Что проверяют
Различаете ли вы передачу объекта по ссылке и передачу ссылки по значению. Из этой путаницы растут баги вида «присвоил новый объект внутри метода, а снаружи остался старый».
Как отвечать
В C# по умолчанию всё передаётся по значению. Для класса копируется ссылка, поэтому параметр и исходная переменная указывают на один объект: изменения его полей видны снаружи, а присваивание нового объекта параметру наружу не выходит.
static void Fill(List<int> x) => x.Add(1); // снаружи видно
static void Replace(List<int> x) => x = new List<int>(); // снаружи не видно
static void Replace(ref List<int> x) => x = new List<int>(); // видноОтсюда набор инструментов:
refпередаёт саму переменную, она должна быть инициализирована до вызова, метод может её переприсвоить;outтоже передаёт переменную, но входное значение не читается, а метод обязан присвоить результат до возврата;inдаёт передачу по ссылке только для чтения, это оптимизация для крупных структур;- для структуры копируется весь экземпляр, поэтому правка полей внутри метода снаружи не видна без
ref; - элементы массива видны всегда, массив это ссылочный тип.
Хороший финал ответа: чаще всего вместо ref и out лучше вернуть значение или кортеж, сигнатура читается проще, а метод остаётся тестируемым.
Где спотыкаются
Заучивают формулировку «классы передаются по ссылке». Передаётся копия ссылки, и проверяется это ровно присваиванием нового объекта внутри метода: без ref вызывающий код изменения не увидит.
Забывают про изменяемые структуры. Поле структуры, полученной из свойства, менять бессмысленно: свойство отдаёт копию, и компилятор такое присваивание не пропустит. Поэтому структуры делают неизменяемыми, а изменяемое состояние держат в классе.
Считают, что readonly и in защищают объект. Они фиксируют только саму ссылку: поля объекта по этой ссылке меняются свободно. Неизменяемость содержимого обеспечивает тип, а не модификатор у переменной.