序列化和产量声明
是否可以序列化包含 yield 语句的方法(或包含此类方法的类),以便在重新水合该类时,保留生成的迭代器的内部状态?
Is it possible to serialize a method containing yield
statements (or a class that contains such a method) such that when you rehydrate the class, the internal state of the generated iterator is retained?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
是的,你可以做到。有警告。
可以在此处找到使用
yield
序列化方法、反序列化和继续的示例:http://www.agilekiwi.com/dotnet/CountingDemo.cs (网络存档链接)。一般来说,尝试序列化而不做一些额外的工作将会失败。这是因为编译器生成的类没有标记
Serialized
属性。不过,您可以解决这个问题。我要指出的是,它们没有标记为可序列化的原因是因为它们是实现细节,并且会在未来版本中发生重大更改,因此您可能无法在较新的版本中反序列化它。
与我在如何序列化匿名委托上提出的问题相关,该问题应该适用于此情况也是如此。
这是“黑客”的源代码:
Yes, you can do this. With caveats.
An example of serializing a method with a
yield
, deserializing and continuing can be found here: http://www.agilekiwi.com/dotnet/CountingDemo.cs (Web Archive Link).In general, trying to serialize without doing some extra work will fail. This is bcause the compiler generated classes are not marked with the
Serializable
attribute. However, you can work around this.I would note the reason that they aren't marked with serializable is because they are an implementation detail and subject to breaking changes in future versions, so you may not be able to deserialize it in a newer version.
Related to a question I asked on how to serialize anonymous delegates, which should work for this case as well.
Here's the source code of the "hack":
在内部,
yield
语句被转换为作为实现 IEnumerator 接口的类实现的状态机。它允许同时使用多个 foreach 语句迭代结果集。该类对您的代码不可见,它未标记为可序列化。所以,答案是否定的,这是不可能的。但是,您可以自行实现所需的枚举器,但它需要比
yield
更多的劳动力。Internally,
yield
statement is transformed to state machine implemented as class that implements IEnumerator interface. It allows to iterate throught resultset using multiple foreach statements at the same time. That class is not visible to your code, it is not marked as serializable.So, answer is no, it is not possible. But, you can implement desired enumerator by itself, but it requires more labor than
yield
.只需确保在调用 Yield 之前,将状态(即迭代器位置)保存在可序列化字段(位置字段,或任何您所说的字段)中。然后,当类被反序列化时,只需使用位置字段从上次停下的地方继续即可。
但是,这什么时候有用呢?您打算在 foreach 循环中间序列化对象吗?如果您为类提供一个默认为当前位置的
SetIteratorPosition()
方法,也许您会变得容易得多。这比向现有的明确定义的行为(yield)添加副作用更清晰,并且每个人都会明白可以保存IteratorPosition
。注意:方法不能序列化。您序列化数据,即属性和字段。
Just make sure that just before you call yield, that you save state (i.e., the iterators position) in a serializable field (the location field, or whatever you call it). Then, when the class is deserialized, simply continue where you left off, using the location field.
But, when will this be useful? Do you plan to serialize objects in the middle of a foreach loop? Maybe you make it a lot easier if you give you class a
SetIteratorPosition()
method, which defaults to the current position. It's clearer than adding side effects to existing well defined behavior (yield) and everyone'll understand thatIteratorPosition
can be saved.Note: methods cannot be serialized. You serialize data, i.e., properties and fields.
是的。任何返回 IEnumerable 的方法都可以有自己的代码,用于
yield return
无论您告诉它什么。如果您序列化对象的内部状态(关于它正在迭代的内容以及迭代的程度),那么您可以在将来的某个时间重新加载该状态,并从您上次停下的地方继续枚举。Yes. Any method that returns an IEnumerable can have it's own code for
yield return
whatever you tell it to. If you serialize the internal state of your object as to what it was iterating and how far it got, then you can reload that state at some future time, and continue the enumeration right where you left off.