Слепо конвертировать структуры в классы, чтобы скрыть конструктор по умолчанию?
-
07-07-2019 - |
Вопрос
Я прочитал все вопросы, связанные с этой темой, и все они объясняют, почему конструктор по умолчанию в struct
недоступен в C#, но я еще не нашел никого, кто предложил бы общий план действий в такой ситуации.
Очевидным решением является простое преобразование struct
к class
и справиться с последствиями.
Есть ли другие варианты сохранить его как struct
?
Я столкнулся с этой ситуацией с одним из наших объектов API внутренней коммерции.Дизайнер переделал его из class
к struct
, и теперь конструктор по умолчанию (который раньше был закрытым) оставляет объект в недопустимом состоянии.
Я подумал, что если мы собираемся сохранить объект как struct
, следует ввести механизм проверки валидности состояния (что-то вроде IsValid
свойство).Я был встречен большим сопротивлением, и объяснение «тот, кто использует API, не должен использовать конструктор по умолчанию», комментарий, который, безусловно, вызвал у меня удивление.(Примечание:рассматриваемый объект создается «правильно» с помощью статических фабричных методов, а все остальные конструкторы internal
.)
Неужели все просто конвертируют свои struct
s для class
es в этой ситуации, не задумываясь?
Редактировать:Я хотел бы увидеть некоторые предложения о том, как сохранить этот тип объекта в качестве struct
-- объект, о котором идет речь выше, гораздо лучше подходит в качестве struct
чем как class
.
Решение
Для struct
, вы проектируете тип таким образом, чтобы созданный по умолчанию экземпляр (все поля были равны нулю) был допустимым состоянием.Вы не [не должны] произвольно использовать struct
вместо class
без уважительной причины — нет ничего плохого в использовании неизменяемого ссылочного типа.
Мои предложения:
- Убедитесь, что причина использования
struct
действителен ([настоящий] профилировщик выявил значительные проблемы с производительностью, возникающие из-за интенсивного выделения очень легкого объекта). - Спроектируйте тип так, чтобы созданный по умолчанию экземпляр был допустимым.
- Если дизайн типа продиктован ограничениями встроенного/COM-взаимодействия, оберните функциональность и не раскрывайте
struct
вне оболочки (частный вложенный тип).Таким образом, вы можете легко документировать и проверять правильность использования требований к ограниченному типу.
Другие советы
Причина этого заключается в том, что структура (экземпляр System.ValueType) обрабатывается специально CLR: она инициализируется со всеми полями, равными 0 (или по умолчанию). Вам даже не нужно его создавать - просто объявите это. Вот почему требуются конструкторы по умолчанию.
Вы можете обойти это двумя способами:
<Ол>Изменение структуры на класс может иметь некоторые очень тонкие последствия (с точки зрения использования памяти и идентичности объектов, которые чаще возникают в многопоточной среде), а также не столь тонкие, но сложные для отладки исключения NullReferenceExceptions для неинициализированных объектов. р>
Причину, по которой невозможно определить конструктор по умолчанию, иллюстрирует следующее выражение:
new MyStruct[1000];
У вас есть 3 варианта здесь
- вызов конструктора по умолчанию 1000 раз или
- создание поврежденных данных (обратите внимание, что структура может содержать ссылки;если вы не инициализируете или не очистите ссылку, вы потенциально можете получить доступ к произвольной памяти), или
- очистить выделенную память нулями (на уровне байтов).
.NET делает то же самое как для структур, так и для классов:поля и элементы массива заполняются нулями.Это также обеспечивает более согласованное поведение между структурами и классами и отсутствие небезопасного кода.Это также позволяет платформе .NET не специализироваться на чем-то вроде new byte[1000]
.
Это конструктор по умолчанию для структур, который .NET требует и позаботится о себе сам:обнулить все байты.
Теперь, чтобы справиться с этим, у вас есть несколько вариантов:
- Добавьте в структуру свойство Am-I-Initialized (например,
HasValue
наNullable
). - Разрешить обнуляемой структуре быть допустимым значением (например, 0 является допустимым значением для десятичной дроби).