Сериализация абстрактного класса
Я пытаюсь сериализовать, и я сталкиваюсь с проблемой с классом abstact.
Я googled для ответа, и я нашел это blogitem. Я пробовал это и эту работу.
хорошо, очень хорошо. Но ознакомьтесь с комментарием к пункту:
эта методика как бы прячется истинная проблема, и это неточная реализация проекта ОО узоры, а именно заводской узор.
изменения базы класс ссылка любой новый класс фабрики саморазрушительной.
немного подумав, код может быть изменен на котором любой производной тип может быть связан с абстрактный класс (через чудо интерфейсы) и никакой XmlInclude не будет требуемый.
Я предлагаю дальнейшее исследование моделей фабрика, которая кажется то, что вы пытаетесь реализовать.
о чем говорит комментатор? Он какой-то неопределенный. Может ли кто-нибудь объяснить это более подробно (на примере)? Или он просто несет чушь?
обновление (после прочтения первого ответа)
почему комментатор говорит о
шаблон фабрики
и
код можно изменить на любой производный тип может быть связан с абстрактный класс (по чудо интерфейсы)
?
Он хочет сделать интерфейс, как это?
public interface IWorkaround
{
void Method();
}
public class SomeBase : IWorkaround
{
public void Method()
{
// some logic here
}
}
public class SomeConcrete : SomeBase, IWorkaround
{
public new void Method()
{
base.Method();
}
}
1 ответов
С BinaryFormatter
, это не проблема; сериализованный поток содержит метаданные полного типа, поэтому, если у вас есть:
[Serializable] abstract class SomeBase {}
[Serializable] class SomeConcrete : SomeBase {}
...
SomeBase obj = new SomeConcrete();
и сериализовать obj
, то он включает в себя "я SomeConcrete
" в потоке. Это делает жизнь простой,но многословной, особенно при повторении. Он также хрупкий, поскольку требует одной и той же реализации при десериализации; плохо для разных реализаций клиента/сервера или для долгосрочного хранения.
С XmlSerializer
(о котором, я думаю, говорит блог), нет метаданных, но имена элементов (или xsi:type
атрибуты) используются, чтобы помочь определить, какой используется. Чтобы это работало, сериализатор необходимо знать заранее какие имена сопоставляются с какими типами.
самый простой способ сделать это-украсить базовый класс подклассами, о которых мы знаем. Затем сериализатор может проверить каждый из них (и любой дополнительные атрибуты xml), чтобы выяснить, что, когда он видит <someConcreteType>
элемент, который соответствует SomeConcrete
экземпляр (обратите внимание, что имена не должны совпадать, поэтому он не может просто искать его по имени).
[XmlInclude(typeof(SomeConcrete))]
public abstract class SomeBase {}
public class SomeConcrete : SomeBase {}
...
SomeBase obj = new SomeConcrete();
XmlSerializer ser = new XmlSerializer(typeof(SomeBase));
ser.Serialize(Console.Out, obj);
однако, если он эстет (или данные не доступны), то есть альтернатива, вы можете указать все эти данные отдельно через перегруженный конструктор к XmlSerializer
. Например, можно найти набор известных подтипов из конфигурация (или, возможно, контейнер IoC) и настройка конструктора вручную. Это не очень сложно, но это достаточно сложно, что это не стоит того, если вы на самом деле это нужно.
public abstract class SomeBase { } // no [XmlInclude]
public class SomeConcrete : SomeBase { }
...
SomeBase obj = new SomeConcrete();
Type[] extras = {typeof(SomeConcrete)}; // from config
XmlSerializer ser = new XmlSerializer(typeof(SomeBase), extras);
ser.Serialize(Console.Out, obj);
кроме того, с XmlSerializer
если вы идете по пользовательскому маршруту ctor, важно кэшировать и повторно использовать XmlSerializer
экземпляр; в противном случае новая динамическая сборка загружается за использование - очень дорого (их нельзя выгрузить). Если вы используете простой конструктор, он кэширует и повторно использует модель, поэтому используется только одна модель.
YAGNI диктует, что мы должны выбрать самый простой вариант; используя [XmlInclude]
устраняет необходимость в сложном конструкторе и устраняет необходимость беспокоиться о кэшировании сериализатора. Другой вариант существует и полностью поддерживается.
Re ваши последующие вопросы:
по "заводской схеме", он говорит о случае, когда ваш код не знаю, о SomeConcrete
, возможно, из-за IoC/DI или аналогичных фреймворков; поэтому у вас может быть:
SomeBase obj = MyFactory.Create(typeof(SomeBase), someArgsMaybe);
который вычисляет соответствующий SomeBase
конкретная реализация, создает экземпляр и передает его обратно. Очевидно, что если наш код не знает о конкретных типах (потому что они указаны только в файле конфигурации), то мы не можем использовать XmlInclude
; но мы можете проанализируйте Данные конфигурации и используйте подход ctor (как указано выше). На самом деле, в большинстве случаев XmlSerializer
используется с объектами POCO/ DTO, поэтому это искусственное беспокойство.
и re интерфейсы; то же самое, но более гибкое (интерфейс не требует иерархии типов). Но!--12--> не поддерживает эту модель. Честно говоря, это не его работа. Его задача-позволить вам хранить и транспортировать данные. Не реализация. Любые объекты, созданные xml-схемой, не будут есть методы. Данные конкретны, а не абстрактны. Пока вы думаете "DTO", дебаты по интерфейсу не являются проблемой. Люди, которых раздражает не возможность использования интерфейсов на их границе не охватила разделение проблем, т. е. они пытаются сделать:
Client runtime entities <---transport---> Server runtime entities
а не менее ограничительных
Client runtime entities <---> Client DTO <--- transport--->
Server DTO <---> Server runtime entities
теперь, во многих (большинство?) случаи ДТО и сущностей can будьте такими же; но если вы пытаетесь сделать что-то, что не нравится транспорту, введите DTO; не боритесь с сериализатором. Та же логика применяется, когда люди изо всех сил пытаются написать свои объект:
class Person {
public string AddressLine1 {get;set;}
public string AddressLine2 {get;set;}
}
как xml формы:
<person>
<address line1="..." line2="..."/>
</person>
если вы хотите этого, intoduce DTO, который соответствует транспорту, и карта между вашей сущностью и DTO:
// (in a different namespace for the DTO stuff)
[XmlType("person"), XmlRoot("person")]
public class Person {
[XmlElement("address")]
public Address Address {get;set;}
}
public class Address {
[XmlAttribute("line1")] public string Line1 {get;set;}
[XmlAttribute("line2")] public string Line2 {get;set;}
}
это касается и всех других мелочей, таких как:
- зачем нужен конструктор без параметров?
- зачем мне нужен сеттер для свойства моей коллекции?
- почему я не могу использовать неизменяемый тип?
- почему мой тип быть публичным?
- как я справляюсь со сложным управлением версиями?
- как обрабатывать разные клиенты с разными макетами данных?
- почему я не могу использовать интерфейсы?
- etc, etc
у вас не всегда есть эти проблемы; но если вы это сделаете-введите DTO (или несколько), и ваши проблемы уйдут. Возвращаясь к вопросу об интерфейсах; типы DTO могут не быть интерфейсными, но ваши типы среды выполнения / бизнеса могут быть.