Источник, на который ссылается 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
является общедоступным, поскольку 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
события, которое я добавил к источнику; Тем не менее, по-прежнему впечатляет заполнить 300 тысяч целых чисел при изменении размера в 15 раз во время операции Просто из любопытства, сколько будет общего времени, чтобы заполнить, если бы я уже знал емкость? 13 мс
Итак, что теперь, если бы Entry
был класс? Будут ли эти времена или показатели действительно сильно отличаться?
Емкость: 312874
MemSize: 2660827 байт
Выполнено Размер: 26мс
Общее время заполнения: 964мс
Очевидно, большая разница заключается в изменении размера. Есть ли разница, если словарь инициализируется с Capacity? Не достаточно, чтобы беспокоиться о ... 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: Конструкция структуры . Короче говоря:
Не предоставляйте конструктор по умолчанию для структуры.
Если структура определяет конструктор по умолчанию, то при создании массивов структуры общеязыковая среда выполнения автоматически выполняет конструктор по умолчанию для каждого элемента массива.
Некоторые компиляторы, такие как компилятор C #, не позволяют структурам иметь конструкторы по умолчанию.
Это на самом деле довольно просто , и мы будем брать из Азимова три закона робототехники :
- Структура должна быть безопасной для использования
- Структура должна эффективно выполнять свою функцию, если это не нарушит правило № 1
- Структура должна оставаться неизменной во время ее использования, если ее уничтожение не требуется для удовлетворения правила # 1
... что мы уберем от этого : короче говоря, будем нести ответственность за использование типов значений. Они бывают быстрыми и эффективными, но имеют способность вызывать много неожиданных действий, если не будут должным образом поддерживаться (например, непреднамеренные копии).
System.Drawing.Rectangle
нарушает все три этих правила.