Когда использовать struct?

Когда вы должны использовать struct, а не класс в С#? Моя концептуальная модель заключается в том, что структуры используются в моменты, когда элемент является просто набором типов значений. Путь к логическому объединению всех их в единое целое.

Я столкнулся с этими правилами here:

  • Структура должна представлять собой единый значение.
  • Структура должна иметь память площадь менее 16 байт.
  • Строка не должна изменяться после создание.

Действуют ли эти правила? Что означает структура семантически?

Ответ 1

Источник, на который ссылается OP, имеет некоторое доверие... но как насчет Microsoft - какова позиция при использовании структуры? Я искал дополнительное обучение из Microsoft, и вот что я нашел:

Рассмотрим определение структуры вместо класса, если экземпляры типа являются небольшими и обычно недолговечными или обычно внедряются в другие объекты.

Не определяйте структуру, если тип имеет все следующие характеристики:

  • Он логически представляет одно значение, подобное примитивным типам (целое, двойное и т.д.).
  • У него размер экземпляра меньше 16 байт.
  • Это неизменный.
  • Его не нужно часто вставлять в бокс.

Microsoft последовательно нарушает эти правила

Хорошо, # 2 и # 3 в любом случае. Наш любимый словарь содержит 2 внутренних структуры:

[StructLayout(LayoutKind.Sequential)]  // default for structs
private struct Entry  //<Tkey, TValue>
{
    //  View code at *Reference Source
}

[Serializable, StructLayout(LayoutKind.Sequential)]
public struct Enumerator : 
    IEnumerator<KeyValuePair<TKey, TValue>>, IDisposable, 
    IDictionaryEnumerator, IEnumerator
{
    //  View code at *Reference Source
}

* Справочный источник

Источник "JonnyCantCode.com" получил 3 из 4 - вполне простительно, поскольку №4, вероятно, не будет проблемой. Если вы обнаружите, что бокс структурирован, переосмыслите свою архитектуру.

Посмотрите, почему Microsoft будет использовать эти структуры:

  • Каждая структура, Entry и Enumerator, представляет собой одиночные значения.
  • Скорость
  • Entry никогда не передается как параметр вне класса Dictionary. Дальнейшее исследование показывает, что для удовлетворения реализации IEnumerable словарь использует структуру Enumerator, которую он копирует каждый раз, когда запрашивается перечислитель... имеет смысл.
  • Внутренний класс словаря. Enumerator является общедоступным, поскольку словарь перечислим и должен иметь равную доступность для реализации интерфейса IEnumerator - например, IEnumerator getter.

Обновить. Кроме того, поймите, что когда структура реализует интерфейс - как это делает Enumerator - и передается в этот реализованный тип, структура становится ссылочным типом и перемещается в кучу. Внутренний класс Dictionary, Enumerator все еще является типом значения. Однако, как только метод вызывает GetEnumerator(), возвращается ссылочный тип IEnumerator.

То, что мы не видим здесь, является попыткой или доказательством необходимости сохранять неизменные структуры или поддерживать размер экземпляра всего 16 байт или меньше:

  • Ничего в вышеприведенных структурах не объявлено readonly - не неизменным
  • Размер этой структуры может превышать 16 байт.
  • Entry имеет неопределенное время жизни (от Add() до Remove(), Clear() или сбор мусора);

И...  4. В обеих структурах хранятся TKey и TValue, которые, как мы все знаем, вполне способны быть ссылочными типами (дополнительная информация о бонусах)

Несмотря на то, что хэшированные ключи, словари бывают отчасти быстрыми, потому что инстанция структуры быстрее, чем ссылочный тип. Здесь у меня есть Dictionary<int, int>, который хранит 300 000 случайных чисел с последовательно увеличивающимися ключами.

Емкость: 312874
MemSize: 2660827 байт
Завершено Изменение размера: 5ms
Общее время для заполнения: 889ms

Емкость: количество элементов, доступных до изменения внутреннего массива.

MemSize: определяется путем сериализации словаря в MemoryStream и получения байтовой длины (достаточно точной для наших целей).

Завершено изменение размера: время, необходимое для изменения размера внутреннего массива от 150862 элементов до 312874 элементов. Когда вы видите, что каждый элемент последовательно копируется через Array.CopyTo(), это не слишком потрепано.

Общее время для заполнения: по общему признанию, искаженное из-за регистрации и событие OnResize, которое я добавил в источник; тем не менее, по-прежнему впечатляет заполнением 300 тыс. целых чисел при изменении размера 15 раз во время операции. Просто из любопытства, каково было бы полное время заполнить, если бы я уже знал способность? 13 мс

Итак, теперь, что, если Entry был классом? Разве эти времена или показатели действительно сильно отличаются?

Емкость: 312874
MemSize: 2660827 байт
Завершено Изменение размера: 26ms
Общее время для заполнения: 964 мс

Очевидно, большая разница заключается в изменении размера. Любая разница, если словарь инициализирован с помощью Емкости? Недостаточно, чтобы вас беспокоило... 12 мс.

Что происходит, потому что Entry - это структура, она не требует инициализации, как ссылочный тип. Это и красота, и проклятие типа значения. Чтобы использовать Entry как ссылочный тип, мне пришлось вставить следующий код:

/*
 *  Added to satisfy initialization of entry elements --
 *  this is where the extra time is spent resizing the Entry array
 * **/
for (int i = 0 ; i < prime ; i++)
{
    destinationArray[i] = new Entry( );
}
/*  *********************************************** */  

Причина, по которой я должен был инициализировать каждый элемент массива Entry в качестве ссылочного типа, можно найти в MSDN: Structure Design. Короче говоря:

Не предоставлять конструктор по умолчанию для структуры.

Если структура определяет конструктор по умолчанию, когда массивы структура, автоматическое время выполнения общего языка выполняет конструктор по умолчанию для каждого элемента массива.

Некоторые компиляторы, такие как компилятор С#, не позволяют структурам имеют конструкторы по умолчанию.

На самом деле это довольно просто, и мы займемся из Азимов Три закона робототехники:

  • Структура должна быть безопасной для использования
  • Структура должна эффективно выполнять свою функцию, если это не будет нарушать правило № 1
  • Структура должна оставаться неповрежденной во время ее использования, если ее уничтожение не требуется для соблюдения правила № 1

... что мы отнимаем от этого: короче говоря, нести ответственность за использование типов значений. Они быстрые и эффективные, но имеют возможность вызывать много неожиданных действий, если они не поддерживаются должным образом (например, непреднамеренные копии).

Ответ 2

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

Ответ 3

Я не согласен с правилами, изложенными в первоначальном посте. Вот мои правила:

1) Вы используете структуры для производительности при хранении в массивах. (см. также Когда строятся ответы?)

2) Вы нуждаетесь в них в передаче кода структурированных данных в/из C/С++

3) Не используйте структуры, если они вам не нужны:

  • Они отличаются от "обычных объектов" (ссылочных типов) при назначении и при передаче в качестве аргументов, что может привести к неожиданному поведению; это особенно опасно, если человек, смотрящий на код, не знаю, что они имеют дело со структурой.
  • Они не могут быть унаследованы.
  • Передача структур как аргументов дороже классов.

Ответ 4

Используйте структуру, если вы хотите семантику значения, а не ссылочную семантику.

Изменить

Не знаю, почему люди сбрасывают это, но это действительная точка, и был сделан до того, как op разъяснил его вопрос, и это самая фундаментальная основная причина для структуры.

Если вам нужна эталонная семантика, вам нужен класс, а не структура.

Ответ 5

В дополнение к ответу "это значение" один конкретный сценарий использования структур - это когда вы знаете, что у вас есть набор данных, которые вызывают проблемы с сборкой мусора, и у вас есть много объектов. Например, большой список/массив экземпляров Person. Естественной метафорой здесь является класс, но если у вас есть большое количество долгоживущих экземпляров Person, они могут в конечном итоге засорить GEN-2 и вызвать GC stalls. Если сценарий оправдывает это, один из возможных подходов здесь - использовать массив (не список) Person structs, т.е. Person[]. Теперь вместо того, чтобы иметь миллионы объектов в GEN-2, у вас есть один кусок на LOH (здесь я не принимаю никаких строк и т.д., Т. Е. Чистое значение без каких-либо ссылок). Это очень мало влияния GC.

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

int index = ...
int id = peopleArray[index].Id;

Обратите внимание, что поддержание неизменных значений поможет здесь. Для более сложной логики используйте метод с параметром by-ref:

void Foo(ref Person person) {...}
...
Foo(ref peopleArray[index]);

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

В очень специфических сценариях эта тактика может быть очень успешной; однако, это довольно продвинутый scernario, который следует попытаться, только если вы знаете, что вы делаете и почему. По умолчанию здесь будет класс.

Ответ 6

Из Спецификация языка С#:

1.7 Структуры

Подобно классам, структуры представляют собой структуры данных, которые могут содержать члены данных и члены функций, но в отличие от классов, структуры типы значений и не требуют распределения кучи. Переменная структуры type непосредственно хранит данные структуры, тогда как переменная class type хранит ссылку на динамически выделенный объект. Типы типов не поддерживают заданное пользователем наследование, а вся структура типы неявно наследуются от объекта типа.

Структуры особенно полезны для небольших структур данных, которые имеют семантика значения. Сложные числа, точки в системе координат или пары ключ-значение в словаре являются хорошими примерами структур. использование структур, а не классов для небольших структур данных может большая разница в количестве распределений памяти приложения выполняет. Например, следующая программа создает и инициализирует массив из 100 баллов. Когда Point реализован как класс, 101 создаются отдельные объекты: один для массива и один для каждого 100 элементов.

class Point
{
   public int x, y;

   public Point(int x, int y) {
      this.x = x;
      this.y = y;
   }
}

class Test
{
   static void Main() {
      Point[] points = new Point[100];
      for (int i = 0; i < 100; i++) points[i] = new Point(i, i);
   }
}

Альтернативой является создание Point a struct.

struct Point
{
   public int x, y;

   public Point(int x, int y) {
      this.x = x;
      this.y = y;
   }
}

Теперь создается только один объект - один для массива - и экземпляры Point хранятся в строке в массиве.

Конструкторы Struct вызываются с новым оператором, но это не означает, что память распределяется. Вместо того, чтобы динамически выделять объект и возвращать ссылку на него, конструктор структуры просто возвращает значение самой структуры (обычно во временном местоположении в стеке), и это значение затем копируется по мере необходимости.

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

Point a = new Point(10, 10);
Point b = a;
a.x = 20;
Console.WriteLine(b.x);

Если Point является классом, результат равен 20, так как a и b ссылаются на один и тот же объект. Если Point - это структура, результат равен 10, потому что назначение a в b создает копию значения, и эта копия не изменяется после последующего назначения a.x.

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

Ответ 7

Структуры хороши для атомного представления данных, где указанные данные могут копироваться многократно по коду. Клонирование объекта в целом более дорогое, чем копирование структуры, поскольку оно включает в себя выделение памяти, запуск конструктора и удаление/сборку мусора при выполнении с ним.

Ответ 8

Вот основное правило.

  • Если все поля-члены являются типами значений, создайте struct.

  • Если какое-либо одно членное поле является ссылочным типом, создайте класс. Это связано с тем, что для поля ссылочного типа в любом случае потребуется распределение кучи.

Exmaples

public struct MyPoint 
{
    public int X; // Value Type
    public int Y; // Value Type
}

public class MyPointWithName 
{
    public int X; // Value Type
    public int Y; // Value Type
    public string Name; // Reference Type
}

Ответ 9

Сначала: сценарии взаимодействия или когда вам нужно указать макет памяти

Во-вторых: когда данные почти совпадают с указателем ссылки.

Ответ 10

Вам нужно использовать "struct" в ситуациях, когда вы хотите явно указать расположение памяти с помощью StructLayoutAttribute - как правило, для PInvoke.

Изменить: комментарий указывает, что вы можете использовать класс или структуру с помощью StructLayoutAttribute, и это, безусловно, верно. На практике, как правило, вы используете структуру - она ​​выделяется в стеке против кучи, что имеет смысл, если вы просто передаете аргумент неуправляемому вызову метода.

Ответ 11

Я использую structs для упаковки или распаковки любого вида бинарного формата связи. Это включает чтение или запись на диск, списки вершин DirectX, сетевые протоколы или обработку зашифрованных/сжатых данных.

Три приведенных вами рекомендаций не были полезны для меня в этом контексте. Когда мне нужно записать четыреста байт материала в определенном порядке, я собираюсь определить структуру из четырехсот байт, и я собираюсь заполнить ее любыми несвязанными значениями, которые она должна иметь, и я собираюсь чтобы настроить его, каким бы то ни было способом в тот момент. (Хорошо, четыреста байт были бы довольно странными - но когда я писал файлы Excel для жизни, я имел дело со структурами длиной до сорока байт, потому что, насколько велики некоторые записи BIFF ARE.)

Ответ 12

За исключением тех значений, которые используются непосредственно средой выполнения и различными другими для целей PInvoke, вы должны использовать только типы значений в двух сценариях.

  • Когда вам нужна семантика копирования.
  • Если вам нужна автоматическая инициализация, обычно в массивах этих типов.

Ответ 13

.NET поддерживает value types и reference types (в Java вы можете определить только ссылочные типы). Экземпляры reference types распределяются в управляемой куче и собираются мусором, когда нет замечательных ссылок на них. С другой стороны, экземпляры value types выделяются в stack, и, следовательно, выделенная память восстанавливается, как только заканчивается их область действия. И, конечно, value types передается по значению, а reference types по ссылке. Все примитивные типы данных С#, за исключением System.String, являются типами значений.

Когда использовать struct over class,

В С#, structs есть value types, классы reference types. Вы можете создавать типы значений в С#, используя ключевое слово enum и ключевое слово struct. Использование value type вместо reference type приведет к уменьшению количества объектов в управляемой куче, что приведет к меньшей нагрузке на сборщик мусора (GC), менее частые циклы GC и, следовательно, лучшую производительность. Однако value types имеют свои недостатки. Обход большой struct определенно дороже, чем передача ссылки, это одна очевидная проблема. Другая проблема связана с служебными данными, связанными с boxing/unboxing. Если вам интересно, что означает boxing/unboxing, следуйте этим ссылкам для хорошего объяснения на boxing и unboxing. Помимо производительности, бывают случаи, когда вам просто нужны типы, чтобы иметь семантику значений, что было бы очень сложно (или уродливо) реализовать, если reference types - все, что у вас есть. Вы должны использовать только value types, когда вам нужна семантика копии или требуется автоматическая инициализация, обычно в arrays этих типов.

Ответ 14

Типы структуры на языках С# или других языках .net обычно используются для хранения вещей, которые должны вести себя как фиксированные группы значений. Полезным аспектом структурных типов является то, что поля экземпляра структурного типа могут быть изменены путем изменения места хранения, в котором оно хранится, и никоим образом. Можно закодировать структуру таким образом, что единственный способ изменить любое поле - построить целый новый экземпляр, а затем использовать назначение структуры для изменения всех полей цели, перезаписав их значениями из нового экземпляра, но если структура не предоставляет никаких средств для создания экземпляра, где его поля имеют значения, отличные от значений по умолчанию, все его поля будут изменчивыми, если и если сама структура хранится в изменяемом расположении.

Обратите внимание, что возможно создать тип структуры, чтобы он по существу вел себя как тип класса, если структура содержит частное поле типа класса и перенаправляет свои собственные члены на объект завернутого класса. Например, PersonCollection может предлагать свойства SortedByName и SortedById, оба из которых содержат "неизменяемую" ссылку на PersonCollection (установленную в их конструкторе) и реализуют GetEnumerator, вызывая либо creator.GetNameSortedEnumerator, либо creator.GetIdSortedEnumerator. Такие структуры будут вести себя как ссылка на PersonCollection, за исключением того, что их методы GetEnumerator будут привязаны к различным методам в PersonCollection. Также можно было бы связать структуру с частью части массива (например, можно было бы определить структуру ArrayRange<T>, которая содержала бы T[], называемую Arr, int Offset и int Length, с индексированным свойство, которое для индекса idx в диапазоне от 0 до Length-1 получило бы доступ к Arr[idx+Offset]). К сожалению, если foo является экземпляром такой структуры только для чтения, текущие версии компилятора не разрешают такие операции, как foo[3]+=4;, потому что у них нет способа определить, будут ли такие операции пытаться писать поля foo.

Также можно создать структуру, которая будет вести себя как тип значения, который содержит коллекцию переменного размера (которая будет копироваться всякий раз, когда будет создана структура), но единственный способ сделать эту работу - обеспечить, чтобы ни один объект к которой структура держит ссылку, когда-либо будет подвергаться воздействию чего-либо, что может ее изменить. Например, можно иметь массивную структуру, которая содержит закрытый массив, а индексированный метод "put" создает новый массив, содержимое которого аналогично содержимому оригинала, за исключением одного измененного элемента. К сожалению, бывает сложно сделать такие структуры эффективными. Хотя есть моменты, когда семантика структуры может быть удобной (например, возможность передать массив-подобную коллекцию в подпрограмму, при этом вызывающий и вызываемый лица, зная, что внешний код не будет изменять коллекцию, могут быть лучше, чем требовать как вызывающего, так и чтобы защищать копии любых данных, которые им даны), требование, чтобы ссылки на классы указывали на объекты, которые никогда не будут мутированы, часто являются довольно серьезным ограничением.

Ответ 15

Нет, я не совсем согласен с правилами. Это хорошие рекомендации, которые следует учитывать при исполнении и стандартизации, но не в свете возможностей.

Как вы можете видеть в ответах, есть много творческих способов их использования. Таким образом, эти рекомендации должны быть просто такими, всегда ради эффективности и эффективности.

В этом случае я использую классы для представления объектов реального мира в своей более крупной форме, я использую structs для представления более мелких объектов, которые имеют более точное использование. То, как вы это сказали, "более сплоченное целое". Ключевое слово является связным. Классы будут больше объектно-ориентированными элементами, а структуры могут иметь некоторые из этих характеристик, хотя и в меньшем масштабе. ИМО.

Я использую их много в тегах Treeview и Listview, где очень часто доступны общие статические атрибуты. Я всегда старался получить эту информацию по-другому. Например, в моих приложениях базы данных я использую Treeview, где у меня есть таблицы, SP, функции или любые другие объекты. Я создаю и заполняю свою структуру, помещаю ее в тег, вытаскиваю, получаю данные выбора и т.д. Я бы не сделал этого с классом!

Я стараюсь держать их маленькими, использовать их в ситуациях с одним экземпляром и не менять их. Разумно осознавать память, распределение и производительность. И тестирование так необходимо.

Ответ 16

A struct - тип значения. Если вы присвойте структуре новую переменную, новая переменная будет содержать копию оригинала.

public struct IntStruct {
    public int Value {get; set;}
}

Выражение следующих результатов в 5 экземплярах структуры, хранящейся в памяти:

var struct1 = new IntStruct() { Value = 0 }; // original
var struct2 = struct1;  // A copy is made
var struct3 = struct2;  // A copy is made
var struct4 = struct3;  // A copy is made
var struct5 = struct4;  // A copy is made

// NOTE: A "copy" will occur when you pass a struct into a method parameter.
// To avoid the "copy", use the ref keyword.

// Although structs are designed to use less system resources
// than classes.  If used incorrectly, they could use significantly more.

A class является ссылочным типом. Когда вы назначаете класс новой переменной, переменная содержит ссылку на исходный объект класса.

public class IntClass {
    public int Value {get; set;}
}

Вывод следующих результатов в только один экземпляр объекта класса в памяти.

var class1 = new IntClass() { Value = 0 };
var class2 = class1;  // A reference is made to class1
var class3 = class2;  // A reference is made to class1
var class4 = class3;  // A reference is made to class1
var class5 = class4;  // A reference is made to class1  

Struct s может увеличить вероятность ошибки кода. Если объект значения обрабатывается как изменяемый ссылочный объект, разработчик может быть удивлен, когда внесенные изменения неожиданно потеряны.

var struct1 = new IntStruct() { Value = 0 };
var struct2 = struct1;
struct2.Value = 1;
// At this point, a developer may be surprised when 
// struct1.Value is 0 and not 1

Ответ 17

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

Итак, сделайте вывод: будьте осторожны с LINQ и скрытыми структурами бокса /unboxing и с помощью структур для микрооптимизации строго остаются с массивами.

PS Другим эталоном относительно передачи структуры/класса через стек вызовов является fooobar.com/questions/5356/...

BenchmarkDotNet=v0.10.8, OS=Windows 10 Redstone 2 (10.0.15063)
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233542 Hz, Resolution=309.2584 ns, Timer=TSC
  [Host] : Clr 4.0.30319.42000, 64bit RyuJIT-v4.7.2101.1
  Clr    : Clr 4.0.30319.42000, 64bit RyuJIT-v4.7.2101.1
  Core   : .NET Core 4.6.25211.01, 64bit RyuJIT


          Method |  Job | Runtime |      Mean |     Error |    StdDev |       Min |       Max |    Median | Rank |  Gen 0 | Allocated |
---------------- |----- |-------- |----------:|----------:|----------:|----------:|----------:|----------:|-----:|-------:|----------:|
   TestListClass |  Clr |     Clr |  5.599 us | 0.0408 us | 0.0382 us |  5.561 us |  5.689 us |  5.583 us |    3 |      - |       0 B |
  TestArrayClass |  Clr |     Clr |  2.024 us | 0.0102 us | 0.0096 us |  2.011 us |  2.043 us |  2.022 us |    2 |      - |       0 B |
  TestListStruct |  Clr |     Clr |  8.427 us | 0.1983 us | 0.2204 us |  8.101 us |  9.007 us |  8.374 us |    5 |      - |       0 B |
 TestArrayStruct |  Clr |     Clr |  1.539 us | 0.0295 us | 0.0276 us |  1.502 us |  1.577 us |  1.537 us |    1 |      - |       0 B |
   TestLinqClass |  Clr |     Clr | 13.117 us | 0.1007 us | 0.0892 us | 13.007 us | 13.301 us | 13.089 us |    7 | 0.0153 |      80 B |
  TestLinqStruct |  Clr |     Clr | 28.676 us | 0.1837 us | 0.1534 us | 28.441 us | 28.957 us | 28.660 us |    9 |      - |      96 B |
   TestListClass | Core |    Core |  5.747 us | 0.1147 us | 0.1275 us |  5.567 us |  5.945 us |  5.756 us |    4 |      - |       0 B |
  TestArrayClass | Core |    Core |  2.023 us | 0.0299 us | 0.0279 us |  1.990 us |  2.069 us |  2.013 us |    2 |      - |       0 B |
  TestListStruct | Core |    Core |  8.753 us | 0.1659 us | 0.1910 us |  8.498 us |  9.110 us |  8.670 us |    6 |      - |       0 B |
 TestArrayStruct | Core |    Core |  1.552 us | 0.0307 us | 0.0377 us |  1.496 us |  1.618 us |  1.552 us |    1 |      - |       0 B |
   TestLinqClass | Core |    Core | 14.286 us | 0.2430 us | 0.2273 us | 13.956 us | 14.678 us | 14.313 us |    8 | 0.0153 |      72 B |
  TestLinqStruct | Core |    Core | 30.121 us | 0.5941 us | 0.5835 us | 28.928 us | 30.909 us | 30.153 us |   10 |      - |      88 B |

Код:

[RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn]
    [ClrJob, CoreJob]
    [HtmlExporter, MarkdownExporter]
    [MemoryDiagnoser]
    public class BenchmarkRef
    {
        public class C1
        {
            public string Text1;
            public string Text2;
            public string Text3;
        }

        public struct S1
        {
            public string Text1;
            public string Text2;
            public string Text3;
        }

        List<C1> testListClass = new List<C1>();
        List<S1> testListStruct = new List<S1>();
        C1[] testArrayClass;
        S1[] testArrayStruct;
        public BenchmarkRef()
        {
            for(int i=0;i<1000;i++)
            {
                testListClass.Add(new C1  { Text1= i.ToString(), Text2=null, Text3= i.ToString() });
                testListStruct.Add(new S1 { Text1 = i.ToString(), Text2 = null, Text3 = i.ToString() });
            }
            testArrayClass = testListClass.ToArray();
            testArrayStruct = testListStruct.ToArray();
        }

        [Benchmark]
        public int TestListClass()
        {
            var x = 0;
            foreach(var i in testListClass)
            {
                x += i.Text1.Length + i.Text3.Length;
            }
            return x;
        }

        [Benchmark]
        public int TestArrayClass()
        {
            var x = 0;
            foreach (var i in testArrayClass)
            {
                x += i.Text1.Length + i.Text3.Length;
            }
            return x;
        }

        [Benchmark]
        public int TestListStruct()
        {
            var x = 0;
            foreach (var i in testListStruct)
            {
                x += i.Text1.Length + i.Text3.Length;
            }
            return x;
        }

        [Benchmark]
        public int TestArrayStruct()
        {
            var x = 0;
            foreach (var i in testArrayStruct)
            {
                x += i.Text1.Length + i.Text3.Length;
            }
            return x;
        }

        [Benchmark]
        public int TestLinqClass()
        {
            var x = testListClass.Select(i=> i.Text1.Length + i.Text3.Length).Sum();
            return x;
        }

        [Benchmark]
        public int TestLinqStruct()
        {
            var x = testListStruct.Select(i => i.Text1.Length + i.Text3.Length).Sum();
            return x;
        }
    }

Ответ 18

Мое правило

1, Всегда используйте класс;

2 Если возникает проблема с производительностью, я пытаюсь изменить какой-либо класс на структуру в зависимости от правил, упомянутых в @IAbstract, а затем выполнить тест, чтобы увидеть, могут ли эти изменения повысить производительность.

Ответ 19

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

Классы и структуры (Руководство по программированию на С#)

Ответ 20

Я думаю, что хорошее первое приближение "никогда".

Я думаю, что хорошее второе приближение "никогда".

Если вы отчаянно нуждаетесь в перфомансе, подумайте о них, но тогда всегда измеряйте.

Ответ 21

Я просто работал с Windows Communication Foundation [WCF] Named Pipe, и я заметил, что имеет смысл использовать Structs, чтобы гарантировать, что обмен данными имеет тип значения вместо ссылочный тип.

Ответ 22

С# struct - легкая альтернатива классу. Он может делать почти то же самое, что и класс, но он менее "дорог" для использования структуры, а не класса. Причина этого немного техническая, но, чтобы подвести итог, новые экземпляры класса помещаются в кучу, где вновь создаваемые структуры размещаются в стеке. Кроме того, вы не имеете ссылок на структуры, например с классами, но вместо этого вы работаете непосредственно с экземпляром struct. Это также означает, что когда вы передаете структуру функции, она используется по значению вместо ссылки. Об этом подробно рассказывается в главе о функциональных параметрах.

Итак, вы должны использовать структуры, если хотите представить более простые структуры данных, и особенно если вы знаете, что вы будете создавать множество из них. В .NET Framework есть много примеров, в которых Microsoft использовала структуры вместо классов, например, Point, Rectangle и Color struct.

Ответ 23

Struct может использоваться для улучшения производительности сбора мусора. Хотя вам обычно не нужно беспокоиться о производительности GC, есть сценарии, где это может быть убийца. Как большие кеши в приложениях с низкой задержкой. См. Это сообщение для примера:

http://00sharp.wordpress.com/2013/07/03/a-case-for-the-struct/

Ответ 24

Структура или типы значений могут использоваться в следующих сценариях -

  • Если вы хотите предотвратить сбор объекта путем сбора мусора.
  • Если это простой тип, и никакая функция-член не изменяет свои поля экземпляра
  • Если нет необходимости выводить из других типов или выводиться на другие типы.

Вы можете узнать больше о типах значений и значениях типа здесь по этой ссылке

Ответ 25

Вкратце, используйте struct if:

1- ваши свойства/поля объекта не нужно изменять. Я имею в виду, что вы просто хотите дать им начальное значение, а затем прочитать их.

2- свойства и поля в вашем объекте - тип значения, и они не такие большие.

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

Ответ 26

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

Как указано в других ответах, я использую классы для объектов реального мира. У меня также есть мышление структур, которые используются для хранения небольших объемов данных.

Ответ 27

Мне кажется, что структура не имеет сильной семантики, которая дает пользователю сильное представление о том, когда его использовать.

Он похож на класс, но больше всего его особенность. Это своего рода униженная версия класса. Много говорится о том, когда не использовать его, но очень мало, когда его использовать.

IMO, нет причин, по которым структура должна быть реализована на языке OO в первую очередь. На самом деле примитивный тип не должен существовать на чистом языке OO, но я отвлекаюсь.

Это может быть способ оптимизировать материал. Это своего рода бокс бесплатно, которые можно оптимизировать на некоторых сайтах.

Мои 2 цента, я бы сказал, что он нарушил KISS принципа языка и избегает его, насколько я могу.

Ответ 28

Структуры в большинстве случаев похожи на классы/объекты. Структура может содержать функции, элементы и может быть унаследована. Но структуры в С# используются только для хранения данных. Структуры занимают меньше ОЗУ, чем классы, и легче собирать сборщик мусора для. Но когда вы используете функции в своей структуре, компилятор фактически принимает эту структуру очень точно так же, как класс/объект, поэтому, если вы хотите что-то с функциями, используйте класс/объект.