簡體   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