Почему нельзя изменить поле структуры, даже если оно является сеттером? С#

Рейтинг: 0Ответов: 1Опубликовано: 16.08.2023

Допустим следующую ситуацию:

struct MyStruct {
  public SomeClass? someObject;
  public SomeClass? SomeObject { get => someObject; set => someObject= value;}
  public void SetObject(SomeClass someObject) => this.someObject = someObject;
}

Если мы получаем структуру например с помощью get реализовав this[] (например простейшая реализация: MyStruct this[int i]{get => arr[i]; set => arr[i] = value;}), то мы по очевидным причинам не можем изменить MyStruct.someObject. Однако метод MyStruct.SetObject(obj) не вызывает ошибки, чего нельзя сказать про MyStruct.SomeObject = obj, хотя по сути они выполняют идентичный код. Можете прояснить, почему setter не работает, а метод работает?

И в целом работая с массивом (MyStruct[] array) мы можем изменять поля структуры (array[0].someObject = ...). Как мне можно реализовать подобный функционал в объекте?

EDIT: Прикрепляю примерный код по просьбе:

class CellData {public int param;} // Его можно переопределять

struct CellContainer{
  /* тут всякие другие поля */
  public CellData? cellData;
  public CellData? CellData {get => cellData; set => cellData = value;}
  public void SetCellData(CellData? cellData) => this.cellData = cellData;
}

class Grid{
  CellContainer[,] grid;
  public CellContainer this[Vector2 pos]
  {
    get => grid[pos.x, pos.y]; 
    set => grid[pos.x, pos.y] = value;
  }
}

Grid grid = …;
grid[pos].cellData = …; // Не работает
grid[pos].CellData = …; // Тоже не работает
grid[pos].SetCellData(…); // Не вызывает ошибок

Хотя по идее SetObject не должен изменять оригинальный объект, а только его копию в... а где она хранится вообще если мы её не сохраняем в стеке

Ответы

▲ 4Принят

Тут всё дело в том, что структуры передаются по значению.

Смотрите, когда вы обращаетесь к свойству типа структуры, "за кадром" происходит вот это:

var temp0 = grid[pos];
temp0.cellData = …;

var temp1 = grid[pos];
temp1.CellData = …;

var temp2 = grid[pos];
temp2.SetCellData(…);

То есть сначала структура копируется, а потом у этой копии вы пытаетесь что-то изменить.

Поскольку эта операция совершенно бессмысленна, компилятор вам и не даёт делать прямые присвоения полям и свойствам. А вот вызов метода компилятор делать разрешает - поскольку не знает что именно этот метод делает.

Как обойти эту проблему? Ну, есть два варианта. Для начала, можно отказаться от структуры и сделать ваш CellContainer классом.

Кроме того, в современных версиях языка можно возвращать ссылку на структуру:

class Grid{
  CellContainer[,] grid;
  public ref CellContainer this[Vector2 pos]
  {
    get => ref grid[pos.x, pos.y]; 
  }
}