На всякий случай вам захочется больше заниматься, и вы обнаружите, что приведенных здесь примеров недостаточно: P
(defun sort-atoms-first-recursive (x &optional y)
(cond
((null x) y)
((consp (car x))
(sort-atoms-first-recursive (cdr x) (cons (car x) y)))
(t (cons (car x) (sort-atoms-first-recursive (cdr x) y)))))
(defun sort-atoms-first-loop (x)
(do ((a x (cdr a))
(b) (c) (d) (e))
(nil)
(if (consp (car a))
(if b (setf (cdr b) a b (cdr b)) (setf b a d a))
(if c (setf (cdr c) a c (cdr c)) (setf c a e a)))
(when (null (cdr a))
(cond
((null d) (return e))
((null c) (return d))
(t (setf (cdr b) nil (cdr c) d) (return e))))))
(sort-atoms-first-recursive '(5 -1 (2 6 1) (8 7 -3) (0 (9 4)) -6))
(sort-atoms-first-loop '(5 -1 (2 6 1) (8 7 -3) (0 (9 4)) -6))
Второй - разрушительный (но не создает никаких новых conses).
Он прав и неправ одновременно.
С такими вещами, как BinaryFormatter
, это не проблема; сериализованный поток содержит метаданные полного типа, поэтому, если у вас есть:
[Serializable] abstract class SomeBase {}
[Serializable] class SomeConcrete : SomeBase {}
...
SomeBase obj = new SomeConcrete();
и сериализован obj
, то он включает в поток «Я SomeConcrete
». Это делает жизнь простой, но многословной, особенно при повторении. Это также непросто, так как требует такой же реализации при десериализации; плохо для различных реализаций клиент / сервер или для долгосрочного хранения.
С XmlSerializer
(о котором, я думаю, говорится в блоге), метаданных нет - но имена элементов (или xsi: type
атрибуты) используются, чтобы помочь определить, что используется. Чтобы это сработало, сериализатору необходимо знать заранее , какие имена соответствуют каким типам.
Самый простой способ сделать это - украсить базовый класс подклассами, о которых мы знаем. Затем сериализатор может проверить каждый из них (и любые дополнительные атрибуты, специфичные для xml), чтобы выяснить, что, когда он видит элемент
, он отображается на экземпляр 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]
устраняет необходимость в сложном конструкторе и устраняет необходимость беспокоиться о кешировании сериализатора. Есть и другой вариант, который, тем не менее, полностью поддерживается.
Ответьте на следующие вопросы:
По "заводскому шаблону", он говорит о случае, когда ваш код не знает о SomeConcrete
, возможно, из-за IoC / DI или подобных фреймворков; так что у вас может быть:
SomeBase obj = MyFactory.Create(typeof(SomeBase), someArgsMaybe);
Который вычисляет соответствующую конкретную реализацию SomeBase
, создает ее экземпляр и возвращает обратно. Очевидно, что если наш код не знает о конкретных типах (потому что они указаны только в файле конфигурации), мы не можем использовать XmlInclude
; но мы можем проанализировать данные конфигурации и использовать подход ctor (как указано выше). На самом деле, в большинстве случаев XmlSerializer
используется с объектами POCO / DTO, так что это искусственная проблема.
И интерфейсы; то же самое, но более гибкое (интерфейс не требует иерархии типов). Но XmlSerializer
не поддерживает эту модель. Честно говоря, жестко; это не его работа. Его задача - позволить вам хранить и передавать данные. Не реализация. Любые объекты, созданные с помощью xml-схемы, не будут иметь методов. Данные конкретны, а не абстрактны. Пока вы думаете «DTO», обсуждение интерфейса не является проблемой. Люди, которых раздражает невозможность использовать интерфейсы на своих границах, не приняли разделения ответственности, то есть они пытаются сделать:
Client runtime entities <---transport---> Server runtime entities
вместо менее ограничительного
Client runtime entities <---> Client DTO <--- transport--->
Server DTO <---> Server runtime entities
Теперь во многих (большинстве?) Случаев DTO и объекты могут совпадать; но если вы пытаетесь сделать что-то, что не нравится транспорту, введите DTO; не борись с сериализатором. Та же логика применяется, когда люди изо всех сил пытаются записать свой объект:
class Person {
public string AddressLine1 {get;set;}
public string AddressLine2 {get;set;}
}
как xml формы:
<person>
<address line1="..." line2="..."/>
</person>
Если вы этого хотите, создайте 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;}
}
Это также относится ко всем другим мелочам, например:
У вас не всегда возникают эти проблемы; но если вы это сделаете - введите DTO (или несколько), и ваши проблемы исчезнут. Возвращаясь к вопросу об интерфейсах; типы DTO могут не основываться на интерфейсах, но ваши типы времени выполнения / бизнеса могут быть.
У вас не всегда возникают эти проблемы ; но если вы это сделаете - введите DTO (или несколько), и ваши проблемы исчезнут. Возвращаясь к вопросу об интерфейсах; типы DTO могут не основываться на интерфейсах, но ваши типы времени выполнения / бизнеса могут быть.
У вас не всегда возникают эти проблемы ; но если вы это сделаете - введите DTO (или несколько), и ваши проблемы исчезнут. Возвращаясь к вопросу об интерфейсах; типы DTO могут не основываться на интерфейсах, но ваши типы времени выполнения / бизнеса могут быть.
t Я использую неизменяемый тип?У вас не всегда возникают эти проблемы; но если вы это сделаете - введите DTO (или несколько), и ваши проблемы исчезнут. Возвращаясь к вопросу об интерфейсах; типы DTO могут не основываться на интерфейсах, но ваши типы времени выполнения / бизнеса могут быть.
t Я использую неизменяемый тип?У вас не всегда возникают эти проблемы; но если вы это сделаете - введите DTO (или несколько), и ваши проблемы исчезнут. Возвращаясь к вопросу об интерфейсах; типы DTO могут не основываться на интерфейсах, но ваши типы времени выполнения / бизнеса могут быть.