Страницы

Поиск по вопросам

воскресенье, 22 декабря 2019 г.

Структуры как поля / свойства класса

#c_sharp #net #struct


Известно, что структуры - это значимые типы, а значит передаются по значению. И это
становится проблемой, когда хочется использовать готовые структуры как поля или свойства
классов. С ними становится невозможно работать, так при попытке изменить какие-то поля
в структуре (которое является свойством или полем), компилятор кричит об ошибке. 

В принципе ничего удивительного, это ожидаемое поведение, вызывающему коду отправляется
копия структуры и компилятор дает понять, что ты изменишь копию, но это ни на что не
повлияет. Это понятно, но как быть? 

Приведу пример:

class Hero
{
  // Структура-поле
  public Point Coordinates;

  public Hero
  {
    Coordinates = new Point(0,0);
  }
}


В данном случае, изменить Coordinates.X из внешнего кода будет невозможно.
Какова практика работы со структурами как полями / свойствами? На вышеуказанном примере,
как изменить структуру? 
    


Ответы

Ответ 1



Для начала, для полей значимых типов вы работаете с самим полем, так что вот такое должно работать ожидаемым образом: Hero hero = new Hero(); hero.Coordinates.X = 100; Но правильнее выставлять вместо поля свойство, и вот там уже вы и правда работаете с копией: class Hero { public Point Coordinates { get; set; } public Hero() { Coordinates = new Point(0,0); } } В этом случае имеет смысл присваивать значение целиком: Hero hero = new Hero(); hero.Coordinates = new Point(100, 0); Дополнение (из дискуссии с автором вопроса в комментариях): для того, чтобы пользователям вашего класса не приходилось думать о таких тонкостях, имеет смысл сделать структуру иммутабельной (то есть, запретить изменять её поля по отдельности), и определить в вашем классе setter (то есть, свойство с акцессором set) для структуры или её частей, который правильно изменит значение. Таким образом, компилятор предотвратит возможную ошибку пользователей вашего класса.

Ответ 2



Структуры обычно нужны для того, чтобы получить возможность выполнить одну операцию, например сравнения или присваивания, над группой данных, в том числе разнородных. Я не хочу сказать что структуры нужно списать в архив, просто многие задачи, которые решались и решаются с помощью структур в других языках, в C# более успешно решают полноценные классы. В исходном коде класса System.Windows.Forms.Control, можно посмотреть реализацию свойства Size, как пример того, как Microsoft использует структуры. У контрола есть поля width и height. Одноименные свойства Width и Height и свойство Size, которое возвращает значения width и height в виде одной структуры типа System.Drawing.Size. Для изменений размеров у вас есть целых два пути: задать ширину и высоту отдельно через соответствующие свойства, либо установить оба размера сразу с помощью структуры Size, например скопированной из другого контрола. Если рассмотреть свойство Location, то найти отдельные свойства для X и Y не получится, их просто нет, хотя внутри используются отдельные поля x и y для хранения координат. В принципе, можно было и внутри использовать структуру, т.к. координаты точки довольно редко используются по-отдельности. На мой взгляд - это достаточно неплохой пример практического применения структур с учетом их особенностей. У Microsoft есть и менее удачные примеры, например свойство Control.Font, в котором кажется довольно странным, не иметь возможности указать, например атрибут шрифта Bold без замены всей структуры Font, но если внимательно присмотреться к исходникам можно заметить, что это свойство связано с неуправляемым кодом WinAPI и скорее всего особого выбора не было, не считая конечно нереального на практике варианта "быстренько запилить новый API". Применяйте имеющиеся инструменты по назначению (в разных языках оно может быть разным, это нормально).

Комментариев нет:

Отправить комментарий