Виды Структур в C#. Начало
Рассмотрим, в чём разница между структурой, структурой для чтения и ref-структурой, а также, какие ограничения есть у этих типов.
1. Структура
Все в той или иной степени ими пользовались. Структура — это тип значения, который в идеале должен представлять одно логическое значение: DateTime, int, double или TimeSpan, либо пользовательские, вроде Point2D, которая может состоять из координат x и y. В документации Microsoft есть подсказка, когда не следует использовать структуры: если экземпляр придётся часто упаковывать.
Обычно типы значений, такие как структура, находятся в стеке. Стек — это хороший способ моделирования времени жизни переменной. Если вы объявите int в функции, он будет жить только в этой области:
public void MyFunc()Здесь a существует только внутри MyFunc, и только на стеке. Но если мы используем какой-либо тип значения, например, в качестве поля в классе, тогда эта переменная должна находиться в куче, поскольку её родитель находится в куче.
{
int a = 2;
}
Структуры позволяют многое из того, что допускает класс, но с некоторыми ключевыми отличиями:
- Структура не может наследовать от других классов или структур (возможны интерфейсы)
- Структура не может иметь деструктор.
Пример структуры:
public struct Color2. Структура только для чтения
{
public byte Red;
public byte Green;
public byte Blue;
public Color() {
Red = 0; Green = 0; Blue = 0;
}
public void ShiftToGrayscale()
{
var avg = (byte)((Red + Green + Blue) / 3);
Red = Green = Blue = avg;
}
}
readonly struct были представлены в C# 7.2. Основная идея состоит в том, чтобы сделать структуру неизменяемой. Конечно, вы можете сделать это без модификатора только для чтения (например, DateTime существует с самого начала), но компилятор поможет вам, если вы нарушите логику только для чтения.
Пример выше выдаст несколько ошибок компилятора, если добавить readonly к объявлению структуры. Причина проста: во-первых, мы должны объявить все поля как readonly (иначе любой вызывающий объект мог бы просто изменить их). Во-вторых, метод ShiftToGrayscale по-прежнему изменяет внутреннее состояние, поэтому его тоже надо изменить на выдачу новой структуры:
public readonly struct ColorНеизменяемость дает много преимуществ:
{
public readonly byte Red;
public readonly byte Green;
public readonly byte Blue;
public Color() {
Red = 0; Green = 0; Blue = 0;
}
public Color(byte red, byte green, byte blue)
{
Red = red; Green = green; Blue = blue;
}
public Color ShiftToGrayscale()
{
var avg = (byte)((Red + Green + Blue) / 3);
return new Color (avg, avg, avg);
}
}
- Повышенная тестируемость
- Потокобезопасность.
- Атомарность неудачи.
- Отсутствие скрытых побочных эффектов.
Компилятор попытается помочь вам с неизменяемостью, но не сможет её гарантировать. Если в структуре есть список, его члены всё равно можно будет изменять:
public readonly struct MyStructОкончание следует…
{
public readonly List<int> myStuff
= new List<int>() { 1, 2 };
public MyStruct() {}
public void Mutate()
{
myStuff[0] = 3;
}
}
Источник: https://steven-giesel.com/blogPost/2a52cd8d-b3b2-42e4-87e8-d6dc14147ddb