繁体   English   中英

C# 如何从产量 function 序列化/反序列化 IEnumerator(到 Json?)

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM