Источник, на который ссылается 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 Dictionary использует структуру Enumerator
, которую он копирует каждый раз, когда запрашивается перечислитель ... имеет смысл.
- Внутри класса Dictionary.
Enumerator
является общедоступным, поскольку Dictionary перечислим и должен иметь равную доступность для реализации интерфейса IEnumerator - например, Получатель IEnumerator.
Обновление. Кроме того, имейте в виду, что когда структура реализует интерфейс - как это делает Enumerator - и приводится к этому реализованному типу, структура становится ссылочным типом и перемещается в кучу. Внутри класса Dictionary Enumerator по-прежнему является типом значения. Однако, как только метод вызывает GetEnumerator()
, возвращается ссылочный тип IEnumerator
.
Чего мы здесь не видим, так это какой-либо попытки или доказательства требования сохранять структуры неизменными или поддерживать размер экземпляра всего 16 байтов или меньше:
- Ничто в приведенных выше структурах не объявлено
readonly
- не неизменным.
- Размер этой структуры может превышать 16 байт.
Entry
имеет неопределенное время жизни (от Add()
до Remove()
, Clear()
или сборка мусора);
И ... 4. Обе структуры хранят TKey и TValue, которые, как мы все знаем, вполне могут быть ссылочными типами (добавлена дополнительная информация).
Несмотря на хешированные ключи, словари работают быстро, отчасти потому, что создание экземпляров структуры выполняется быстрее, чем ссылочного типа. Здесь у меня Dictionary<int, int>
, в котором хранится 300 000 случайных целых чисел с последовательно увеличивающимися ключами.
Емкость: 312874
MemSize: 2660827 байт
Завершенное изменение размера: 5 мс
Общее время заполнения: 889 мс
Емкость: количество доступных элементов до изменения размера внутреннего массива.
MemSize: определяется путем сериализации словаря в MemoryStream и получения байтовой длины (достаточно точной для наших целей).
Завершенное изменение размера: время, необходимое для изменения размера внутреннего массива с 150862 элементов до 312874 элементов. Когда вы думаете, что каждый элемент последовательно копируется через Array.CopyTo()
, это не так уж плохо.
Общее время заполнения: предположительно искажено из-за регистрации и OnResize
события, которое я добавил в источник; тем не менее, все еще впечатляет заполнение 300k целых чисел при 15-кратном изменении размера во время операции. Просто из любопытства, сколько бы было времени на заполнение, если бы я уже знал емкость? 13 мс
Итак, что, если бы Entry
был классом? Неужели эти времена или метрики действительно так сильно различаются?
Емкость: 312874
MemSize: 2660827 байт
Завершенное изменение размера: 26 мс
Общее время заполнения: 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 < / а>. Суммируя:
Не указывайте конструктор по умолчанию для структуры.
Если структура определяет конструктор по умолчанию, при создании массивов структуры среда CLR автоматически выполняет конструктор по умолчанию для каждого элемента массива.
Некоторые компиляторы, такие как компилятор C #, не позволяют структурам иметь конструкторы по умолчанию.
На самом деле это довольно просто, и мы позаимствуем его из Трех законов робототехники Азимова:
- Структура должна быть безопасной для использования
- Структура должна эффективно выполнять свою функцию, если это не нарушит правило №1.
- Структура должна оставаться нетронутой во время использования, если только ее уничтожение не требуется для выполнения правила №1.
... что мы извлекаем из этого: короче говоря, ответственно относитесь к использованию типов значений. Они быстрые и эффективные, но могут вызывать множество неожиданных действий, если их не поддерживать должным образом (например, непреднамеренные копии).
person
IAbstract
schedule
07.08.2011
System.Drawing.Rectangle
нарушает все три правила. - person ChrisW   schedule 06.02.2009Rectangle
в том, что его члены являются свойствами, а не полями. Использование свойств struct, когда полей было бы достаточно, вероятно, на 95% ответственно за изменяемые структуры, является злым понятием (есть законное использование для установщиков свойств в экземплярах структур только для чтения; из-за этого C # только недавно начал запрещать их использование (исключая законные варианты использования); структуры, которые обертывали члены в свойствах чтения и записи, эффективно превращали код, который должен был и должен был генерировать ошибки компилятора, в код, который будет компилироваться, но не работать. - person supercat   schedule 24.12.2012System.Drawing.Rectangle
не представляет собой одно значение? Не могли бы вы объяснить это? - person Marson Mao   schedule 28.07.2015Rectangle
: что-то, что определенно должно быть структурой, но нарушает все это. Таким образом, это не очень хорошие условия. - person Wolfzoon   schedule 11.09.2016