C# 序列化和Yield语句
是否可以序列化包含C# 序列化和Yield语句,c#,.net,yield,continuations,yield-return,C#,.net,Yield,Continuations,Yield Return,是否可以序列化包含yield语句的方法(或包含此类方法的类),以便在对该类重新水化时,所生成迭代器的内部状态保留?内部,yield语句被转换为作为实现IEnumerator接口的类实现的状态机。它允许同时使用多个foreach语句遍历resultset。该类对代码不可见,未标记为可序列化 所以,答案是否定的,这是不可能的。但是,您可以自行实现所需的枚举数,但它需要比yield更大的工作量,只需确保在调用yield之前,将状态(即迭代器位置)保存在可序列化字段(位置字段,或任何您称之为的字段)中即
yield
语句的方法(或包含此类方法的类),以便在对该类重新水化时,所生成迭代器的内部状态保留?内部,yield
语句被转换为作为实现IEnumerator接口的类实现的状态机。它允许同时使用多个foreach语句遍历resultset。该类对代码不可见,未标记为可序列化
所以,答案是否定的,这是不可能的。但是,您可以自行实现所需的枚举数,但它需要比
yield
更大的工作量,只需确保在调用yield之前,将状态(即迭代器位置)保存在可序列化字段(位置字段,或任何您称之为的字段)中即可。然后,当类被反序列化时,只需使用location字段继续您离开的地方
但是,这什么时候有用呢?是否计划在FURACH循环的中间序列化对象?如果给类aSetIteratorPosition()
method(默认为当前位置),可能会使它变得更容易。这比向现有的定义良好的行为(收益率)添加副作用更清楚,每个人都会明白,IteratorPosition
可以被保存
注意:无法序列化方法。您可以序列化数据,即属性和字段。是。任何返回IEnumerable的方法都可以有自己的代码,用于
yield return
您告诉它的任何内容。如果将对象的内部状态序列化为迭代的内容及其到达的距离,则可以在将来某个时间重新加载该状态,并在结束时继续枚举。是的,可以这样做。注意事项。
使用产生、反序列化和继续序列化方法的示例可在此处找到:()
通常,尝试序列化而不做额外的工作将失败。这是因为编译器生成的类没有标记为Serializable
属性。但是,您可以解决这个问题
我要指出的是,它们没有标记为serializable的原因是,它们是一个实现细节,在未来的版本中可能会发生中断性的更改,因此您可能无法在较新的版本中对其进行反序列化
与我提出的一个问题有关,这个问题也适用于本案例
以下是“黑客”的源代码:
//版权所有©2007 John M Rusk(http://www.agilekiwi.com)
//
//您可以根据需要以任何方式使用此源代码
//以下情况:
//
//(a)上述版权公告和本许可公告应:
//包含在软件的所有副本或重要部分中。
//
//(b)软件按“原样”提供,无任何形式的担保,
//明示或暗示,包括但不限于保证
//适销性、特定用途的适用性和
//不干涉。在任何情况下,作者或版权
//持有人应承担任何索赔、损害赔偿或其他责任,
//无论是在合同诉讼、侵权诉讼还是其他诉讼中
//来自、来自或与软件或使用有关;或
//软件中的其他交易。
使用制度;
使用System.Collections.Generic;
使用系统诊断;
使用System.IO;
运用系统反思;
使用System.Runtime.Serialization;
使用System.Runtime.Serialization.Formatters.Soap;
命名空间AgileKiwi.PersistentIterator.Demo
{
///
///这是我们将要列举的课程
///
[可序列化]
公共类simplenumerable
{
公共IEnumerator Foo()
{
收益率回报率“一”;
收益率回报率“二”;
收益率回报率“三”;
}
#这里的区域是一个更高级的示例
//这表明该解决方案甚至适用于调用其他迭代器的迭代器
//有关更简单的示例,请参见下面的SimpleFo
公共IEnumerator AdvancedFoo()
{
收益率回报率“一”;
foreach(字符串s以字母()表示)
收益率回报率“两”+s;
收益率回报率“三”;
}
私人IEnumerable字母()
{
收益率回报率“a”;
收益率回报率“b”;
收益率回报率“c”;
}
#端区
}
///
///这是调用迭代器并序列化状态的命令行程序
///
公共课程
{
公共静态void Main()
{
//创建/恢复迭代器
IEnumerator e;
if(File.Exists(StateFile))
e=加载迭代器();
其他的
e=(新的simplenumerable()).Foo();//启动新的迭代器
//移动到下一个项目并显示它。
//我们不能在这里使用foreach,因为我们只想要一个
//一次一个结果。
if(如MoveNext())
控制台写入线(e.Current);
其他的
WriteLine(“已完成。删除state.xml文件以重新启动”);
//将迭代器状态保存回文件
保存迭代器(e);
//如果从IDE运行,请暂停
if(Debugger.IsAttached)
{
控制台。写入(“按任意键…”);
Console.ReadKey();
}
}
静态字符串状态文件
{
得到{
返回路径。联合收割机(
Path.GetDirectoryName(Assembly.GetEntryAssembly().Location),
“State.xml”);
}
}
静态IEnumerator LoadIterator()
{
使用(FileStream-stream=newfilestream(StateFile,FileMode.Open))
{
ISurrogateSelector选择器=新的E
// Copyright © 2007 John M Rusk (http://www.agilekiwi.com)
//
// You may use this source code in any manner you wish, subject to
// the following conditions:
//
// (a) The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// (b) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Soap;
namespace AgileKiwi.PersistentIterator.Demo
{
/// <summary>
/// This is the class we will enumerate over
/// </summary>
[Serializable]
public class SimpleEnumerable
{
public IEnumerator<string> Foo()
{
yield return "One";
yield return "Two";
yield return "Three";
}
#region Here is a more advanced example
// This shows that the solution even works for iterators which call other iterators
// See SimpleFoo below for a simpler example
public IEnumerator<string> AdvancedFoo()
{
yield return "One";
foreach (string s in Letters())
yield return "Two " + s;
yield return "Three";
}
private IEnumerable<string> Letters()
{
yield return "a";
yield return "b";
yield return "c";
}
#endregion
}
/// <summary>
/// This is the command-line program which calls the iterator and serializes the state
/// </summary>
public class Program
{
public static void Main()
{
// Create/restore the iterator
IEnumerator<string> e;
if (File.Exists(StateFile))
e = LoadIterator();
else
e = (new SimpleEnumerable()).Foo(); // start new iterator
// Move to next item and display it.
// We can't use foreach here, because we only want to get ONE
// result at a time.
if (e.MoveNext())
Console.WriteLine(e.Current);
else
Console.WriteLine("Finished. Delete the state.xml file to restart");
// Save the iterator state back to the file
SaveIterator(e);
// Pause if running from the IDE
if (Debugger.IsAttached)
{
Console.Write("Press any key...");
Console.ReadKey();
}
}
static string StateFile
{
get {
return Path.Combine(
Path.GetDirectoryName(Assembly.GetEntryAssembly().Location),
"State.xml");
}
}
static IEnumerator<string> LoadIterator()
{
using (FileStream stream = new FileStream(StateFile, FileMode.Open))
{
ISurrogateSelector selector = new EnumerationSurrogateSelector();
IFormatter f = new SoapFormatter(selector, new StreamingContext());
return (IEnumerator<string>)f.Deserialize(stream);
}
}
static void SaveIterator(IEnumerator<string> e)
{
using (FileStream stream = new FileStream(StateFile, FileMode.Create))
{
ISurrogateSelector selector = new EnumerationSurrogateSelector();
IFormatter f = new SoapFormatter(selector, new StreamingContext());
f.Serialize(stream, e);
}
#region Note: The above code puts the name of the compiler-generated enumerator class...
// into the serialized output. Under what circumstances, if any, might a recompile result in
// a different class name? I have not yet investigated what the answer might be.
// I suspect MS provide no guarantees in that regard.
#endregion
}
}
#region Helper classes to serialize iterator state
// See http://msdn.microsoft.com/msdnmag/issues/02/09/net/#S3
class EnumerationSurrogateSelector : ISurrogateSelector
{
ISurrogateSelector _next;
public void ChainSelector(ISurrogateSelector selector)
{
_next = selector;
}
public ISurrogateSelector GetNextSelector()
{
return _next;
}
public ISerializationSurrogate GetSurrogate(Type type, StreamingContext context, out ISurrogateSelector selector)
{
if (typeof(System.Collections.IEnumerator).IsAssignableFrom(type))
{
selector = this;
return new EnumeratorSerializationSurrogate();
}
else
{
//todo: check this section
if (_next == null)
{
selector = null;
return null;
}
else
{
return _next.GetSurrogate(type, context, out selector);
}
}
}
}
// see http://msdn.microsoft.com/msdnmag/issues/02/09/net/#S3
class EnumeratorSerializationSurrogate : ISerializationSurrogate
{
public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
{
foreach(FieldInfo f in obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
info.AddValue(f.Name, f.GetValue(obj));
}
public object SetObjectData(object obj, SerializationInfo info, StreamingContext context,
ISurrogateSelector selector)
{
foreach (FieldInfo f in obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
f.SetValue(obj, info.GetValue(f.Name, f.FieldType));
return obj;
}
}
#endregion
}