抽象类的序列化
我正在尝试序列化,但遇到了抽象类的问题。
我在 Google 上搜索答案,找到了此博客文章 。 我试过了,而且有效。
好的,非常好。但请查看该项目的评论:
这个方法论似乎是隐藏的 真正的问题是 OO设计的不准确实现 模式,即工厂模式。
必须将基类更改为 引用任何新工厂类是 弄巧成拙。
稍微思考一下,代码 可以更改为任何派生的地方 类型可以与 抽象类(通过奇迹 接口)并且没有 XmlIninclude 是 必填。
我建议进一步研究 工厂模式似乎是 你想在这里实现什么。
评论者在说什么?他有点模糊。有人可以更详细地解释一下(举例)吗?还是他只是在胡说八道?
更新(阅读第一个答案后)
评论者为什么谈论
工厂模式
和
代码可以更改为任意位置 派生类型可以与 抽象类(通过 界面奇迹)
?
他想做一个这样的界面吗?
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();
}
}
I'm trying to serialize, and I am facing a problem with an abstact class.
I googled for an answer, and I found this blogitem.
I tried that and that work.
Ok, very nice. But check out the comment on the item:
This methodology seems to be hiding
the true problem and that is an
inaccurate implementation of OO design
patterns, namely the factory pattern.Having to change the base class to
reference any new factory class is
self-defeating.With a little after-thought, the code
can be changed to where any derived
type can be associated with the
abstract class (through the miracle of
interfaces) and no XmlInclude would be
required.I suggest further research into
factory patterns which seems to be
what you are trying to implement here.
What is commenter talking about? He is kinda vague. Can someone explain it more in detail (with an example)? Or is he just talking nonsense?
Update (after reading the first answer)
Why does the commentor talk about
factory pattern
and
the code can be changed to where any
derived type can be associated with
the abstract class (through the
miracle of interfaces)
?
Does he want to make an interface, like this?
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();
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
他既是对的,又是错的。
对于像
BinaryFormatter
这样的东西,这不是问题;序列化流包含完整类型元数据,因此如果您有:并序列化
obj
,那么它会在流中包含“I'm aSomeConcrete
”。这使生活变得简单,但也很冗长,尤其是在重复时。它也很脆弱,因为它在反序列化时需要相同的实现;对于不同的客户端/服务器实现或长期存储来说都是不利的。对于 XmlSerializer(我猜博客正在谈论它),没有元数据 - 但元素名称(或 xsi:type 属性)用于帮助识别哪些元数据被使用。为此,序列化器需要提前知道哪些名称映射到哪些类型。
最简单的方法是用我们知道的子类来装饰基类。然后,序列化程序可以检查其中的每一个(以及任何其他特定于 xml 的属性),以确定当它看到
元素时,该元素映射到SomeConcrete
实例(请注意,名称不需要匹配,因此不能仅按名称查找)。然而,如果他是一个纯粹主义者(或者数据不可用),那么还有一个替代方案;您可以通过 XmlSerializer 的重载构造函数单独指定所有这些数据。例如,您可以从配置(或者可能是 IoC 容器)中查找一组已知子类型,并手动设置构造函数。这并不是很棘手,但它已经足够棘手了,除非您真正需要它,否则不值得这样做。
此外,对于
XmlSerializer
,如果您采用自定义构造器路线,则缓存和重用XmlSerializer
实例非常重要;否则,每次使用都会加载一个新的动态程序集 - 非常昂贵(它们无法卸载)。如果您使用简单的构造函数,它会缓存并重用模型,因此仅使用单个模型。YAGNI 规定我们应该选择最简单的选项;使用
[XmlInclude]
无需使用复杂的构造函数,也无需担心缓存序列化程序。不过,还有另一个选项并且得到完全支持。关于您的后续问题:
通过“工厂模式”,他谈论的是您的代码不知道
SomeConcrete
的情况,可能是由于IoC/DI或类似的原因框架;所以你可能会:找出适当的
SomeBase
具体实现,实例化它并将其返回。显然,如果我们的代码不知道具体类型(因为它们仅在配置文件中指定),那么我们就不能使用XmlInclude
;但我们可以解析配置数据并使用ctor方法(如上所述)。实际上,大多数时候XmlSerializer
与 POCO/DTO 实体一起使用,因此这是一个人为的担忧。并重新接口;相同的事情,但更灵活(接口不需要类型层次结构)。但
XmlSerializer
不支持此模型。坦白说,很难;那不是它的工作。它的工作是允许您存储和传输数据。不执行。任何 xml 模式生成的实体都不会具有方法。数据是具体的,而不是抽象的。只要你想到“DTO”,接口争论就不是问题。因无法在其边界上使用接口而烦恼的人们还没有接受关注点分离,即他们正在尝试做:而不是限制较少的
现在,在许多(大多数?)情况下,DTO 和实体 可以相同;但如果你试图做一些传输不喜欢的事情,请引入 DTO;不要对抗序列化器。当人们努力编写他们的对象时,同样的逻辑也适用:
作为表单的 xml:
如果你想要这个,引入一个与传输相对应的 DTO,并在你的实体和 DTO 之间进行映射:
这也适用于所有其他问题比如:
你并不总是遇到这些问题;但如果你这样做了——引入一个(或多个)DTO,你的问题就会消失。回到关于接口的问题; DTO 类型可能不是基于接口的,但您的运行时/业务类型可以是。
He is both right and wrong at the same time.
With things like
BinaryFormatter
, this is a non-issue; the serialized stream contains full type metadata, so if you have:and serialize
obj
, then it includes "I'm aSomeConcrete
" in the stream. This makes life simple, but is verbose, especially when repeated. It is also brittle, as it demands the same implementation when deserializing; bad for either different client/server implementations, or for long-term storage.With
XmlSerializer
(which I guess the blog is talking about), there is no metadata - but the element names (or thexsi:type
attributes) are used to help identify which is used. For this to work, the serializer needs to know in advance what names map to which types.The simplest way to do this is to decorate the base-class with the subclasses we know about. The serializer can then inspect each of these (and any additional xml-specific attributes) to figure out that when it sees a
<someConcreteType>
element, that maps to aSomeConcrete
instance (note that the names don't need to match, so it can't just look for it by name).However, if he is a purist (or the data isn't available), then there is an alternative; you can specify all this data separately via the overloaded constructor to
XmlSerializer
. For example, you might lookup the set of known subtypes from configuration (or maybe an IoC container), and setup the constructor manually. This isn't very tricky, but it is tricky enough that it isn't worth it unless you actually need it.Additionally, with
XmlSerializer
if you go the custom ctor route, it is important to cache and re-use theXmlSerializer
instance; otherwise a new dynamic assembly is loaded per usage - very expensive (they can't be unloaded). If you use the simple constructor it caches and re-uses the model, so only a single model is used.YAGNI dictates that we should choose the simplest option; using
[XmlInclude]
removes the need for a complex constructor, and removes the need to worry about caching the serializer. The other option is there and is fully supported, though.Re your follow-up questions:
By "factory pattern", he's talking about the case where your code doesn't know about
SomeConcrete
, perhaps due to IoC/DI or similar frameworks; so you might have:Which figures out the appropriate
SomeBase
concrete implementation, instantiates it and hands it back. Obviously, if our code doesn't know about the concrete types (because they are only specified in a config file), then we can't useXmlInclude
; but we can parse the config data and use the ctor approach (as above). In reality, most timesXmlSerializer
is used with POCO/DTO entities, so this is an artificial concern.And re interfaces; same thing, but more flexible (an interface doesn't demand a type hierarchy). But
XmlSerializer
doesn't support this model. Frankly, tough; that isn't its job. Its job is to allow you to store and transport data. Not implementation. Any xml-schema generated entities won't have methods. Data is concrete, not abstract. As long as you think "DTO", the interface debate is a non-issue. People who are vexed by not being able to use interfaces on their boundary haven't embraced separation of concerns, i.e. they are trying to do:rather than the less restrictive
Now, in many (most?) cases the DTO and entities can be the same; but if you are trying to do something that the transport doesn't like, introduce a DTO; don't fight the serializer. The same logic applies when people are struggling to write their object:
as xml of the form:
If you want this, intoduce a DTO that corresponds to the transport, and map between your entity and the DTO:
This also applies to all those other niggles like:
You don't always have these problems; but if you do - introduce a DTO (or several) and your problems go away. Taking this back to the question about interfaces; the DTO types might not be interface-based, but your runtime/business types can be.