[英]C# How to Serialize/Deserialize an IEnumerator from a Yield function (to Json?)
我正在尝试使用 yield 序列化/反序列化从 function 生成的 IEnumerator。 我想在任何迭代中序列化 IEnumerator,我不想强迫它生成所有它的值。
我知道yield
关键字会在幕后生成一个 class ,这就是我使用它的原因,以避免手动编写迭代器,也使代码更清晰。
我的目标是制作一个类似于Nick Gravelyn - The magic of yield的小型游戏引擎,其中每个 GameElement 生成一个关于他的行为的迭代器,允许程序员轻松控制时间(因为迭代器允许中断和继续脚本)。 我想尝试在此之上添加一个多人游戏层,这就是我需要序列化/反序列化 IEnumerator 的原因。
强制 IEnumerator 生成他的所有值也会强制游戏更新,应该避免这种情况。
我的第一个尝试是这样的:
using System;
using System.Collections.Generic;
using System.Text.Json;
namespace SerializeTest
{
class Program
{
public static IEnumerator<int> CountTo(int end)
{
for(int i = 1; i <= end; i++)
{
Console.WriteLine("i = " + i);
yield return i;
}
}
static void Main(string[] args)
{
IEnumerator<int> e = CountTo(5);
string json = JsonSerializer.Serialize(e);
Console.WriteLine(json);
var f = JsonSerializer.Deserialize<IEnumerator<int>> (json); // crash because interface
while (f.MoveNext());
}
}
}
但是反序列化一个接口( IEnumerator<int>
)是非法的,我们需要知道IEnumerator<int>
后面的 object 的Type
。
由于该类型是由编译器生成的,我尝试在Deserialize
方法上使用反射,以便使用 function CountTo()
生成的正确类型来调用它(如果你好奇, SerializeTest.Program+<CountTo>d__0
)
所以我的第二个尝试是这样的:
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.Json;
namespace SerializeTest
{
class Program
{
public static IEnumerator<int> CountTo(int end)
{
for(int i = 1; i <= end; i++)
{
Console.WriteLine("i = " + i);
yield return i;
}
}
public static T Deserialize<T>(string json) => JsonSerializer.Deserialize<T>(json); // still crash
static void Main(string[] args)
{
IEnumerator<int> e = CountTo(5);
string json = JsonSerializer.Serialize(e);
Console.WriteLine(json);
Type eType = e.GetType();
MethodInfo method = typeof(Program).GetMethod("Deserialize");
MethodInfo genericMethod = method.MakeGenericMethod(eType);
object fObj = genericMethod.Invoke(null, new object[] { json });
var f = (IEnumerator<int>)fObj;
while (f.MoveNext()) ;
}
}
}
但是在调用JsonSerializer.Deserialize()
时它仍然会崩溃,即使类型现在是正确的而不是接口。
( System.InvalidOperationException : 'Each parameter in constructor 'Void.ctor(Int32)' on type 'SerializeTest.Program+<CountTo>d__0' must bind to an object property or field on deserialization. Each parameter name must match with a property or field on the object. The match can be case-insensitive.'
)
格式没有任何重要性(json、xml...),只要可以将其发送到另一台计算机并反序列化即可。
我天真地希望有一种方法可以对 IEnumerator 进行序列化/反序列化,因为可以用它来做很多很酷的事情,尽管我对这样的事情是否可能有一些严重的怀疑。
感谢您到目前为止的阅读。
我天真地希望有一种方法可以对 IEnumerator 进行序列化/反序列化,因为可以用它来做很多很酷的事情,尽管我对这样的事情是否可能有一些严重的怀疑。
发送代码(以及您尝试发送的编译器生成的枚举器 state 机器基本上是代码)比将其序列化为 json/xml/etc 要复杂得多。 例如 Apache Ignite支持将代码发送到另一个节点,包括程序集对等加载。 调查的起点之一可以在这里。
至于您的尝试,您应该已经看到 state 机器的序列化版本只包含一个属性 - Current
: {"Current":0}
而生成的 class包含一些其他的 Z9ED39E2EA931586B3A985A6942EF5
private sealed class <<<Main>$>g__CountTo|0_0>d : IEnumerator<int>, IEnumerator, IDisposable
{
private int <>1__state;
private int <>2__current;
public int end;
private int <i>5__1;
int IEnumerator<int>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
// ... rest of the generated code
}
虽然您可以考虑编写自己的自定义转换器,该转换器将使用反射来实际序列化/反序列化内部 state 数据(这可能并不难),但它可能非常脆弱(类名可以更改,生成的代码可以更改,代码生成器代码可以更改,我们甚至还没有开始支持多版本客户端),最后这些字段是私有的,所以我建议查看多人客户端将相互发送的一组消息,你可以转它们进入迭代器、类、方法调用等。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.