5

私はJavaゲームをC#(PuppyGamesによるTitanAttacks)に変換しており、保存ファイルのゲーム状態のシリアル化である最後のタスクを除けば、ほぼ完了しています。

典型的な階層:リソース(ベース)->機能->画面/効果/エンティティ->ゲームスクリーン/レーザー効果/侵入者

Javaコードは、標準のObjectOutputStream / ObjectInputStreamを使用してバイナリシリアル化を実行しますが、基本クラスレベル(リソース)でreadResolve / writeResolve作業を実行して、シリアル化プロセスをカスタマイズします(リソースに名前が付けられている場合は、シリアル化せず、単にプロキシを返します)後でハッシュマップからリソースをフェッチするために使用される名前を使用します)。

私の素朴な解決策は、このアプローチを盲目的にコピーし、基本クラスにISerializableを実装して、TYPEをオーバーライドすることです...

public virtual void GetObjectData(SerializationInfo info, StreamingContext context) {
    if (name != null) {
        // Got a name, so send a SerializedResource that just references us
        info.AddValue("name", this.name);
        info.SetType(typeof(SerializedResource));
        return;
    }

    //Serialize just MY fields (defined in Resource)
    this.SerializeMyFields(info, context, typeof(Resource));
}

Q)それで、組み込みのシリアル化にはすべての賭けがオフになっていると確信しています。シリアル化コンストラクターとともに、継承チェーンの最後までISerializableを実装する必要がありますか?

注GetObjectDataは仮想であるため、派生クラスはフィールドをシリアル化してから基本クラスを呼び出すことができます。これは機能しますが、多くのクラス(100)に追加する退屈な作業がたくさんあります。

一部の派生型(Sprite、InvaderBehaviourなど)もカスタムシリアル化作業を実行して、問題を悪化させます。

このテーマに関するJeffreyRichterの記事を見て、代わりにResourceSurrogateSelector:ISerializationSurrogate型構文を使用してみましたが、これらのシリアル化メソッドは、シリアル化される型がリソースであり、リソースから派生した型ではない場合にのみ呼び出されます(つまり、シリアル化とは呼ばれません)。インベーダーまたはゲームスクリーン)

Q)これを行うための賢い方法はありますか?

私は2つのコードベースを互いにかなり近くに保つことができ、これにより変換がはるかに簡単になりました-本当にやむを得ない理由がない限り、ここでこのアプローチを継続したいと思います(XmlSerializer、Protobufなどはありません)。に。

プロセスを自動化し、Serializableインターフェイスを実装するタイプを反映し、メインクラスファイルを汚染しないようにすべての.Netシリアル化コードを含む.csファイルを作成するJavaを作成することを考えました(それらを部分的にする)

PS-ターゲットプラットフォームは、.Net側のWindows8 / Surface / XBox360(バージョン4)であり、おそらくPSVita/おそらくMonoを使用するiOSです。保存は、シリアル化されたプラットフォームで逆シリアル化されます。

編集 この投稿のSergeyTeplyakovによる回答..... NET、C#:ISerializableインターフェイスとして機能するカスタムシリアル化属性を追加する方法...目的の選択に役立つように見えるISurrogateSelectorインターフェイスに私を導きました派生クラス。

4

1 に答える 1

1

これは私がこれまでに思いついたことであり、かなり満足しています:-) readResolve/writeReplace を追加するだけで、ほぼ完了です! (おそらく、適切な測定のために、Object、SerializationInfo、StreamingContext 引数を ObjectOutputStream にラップします)。

using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

using java.io; //My implementation of various Java classes

namespace NewSerializationTest {

public sealed class JavaSerializableSurrogateSelector : ISurrogateSelector
{
    public void ChainSelector(ISurrogateSelector selector) { throw new NotImplementedException(); }

    public ISurrogateSelector GetNextSelector() { throw new NotImplementedException(); }

    public ISerializationSurrogate GetSurrogate(Type type, StreamingContext context, out ISurrogateSelector selector)
    {
        if (typeof(Serializable).IsAssignableFrom(type))
        {
            selector = this;
            return new JavaSerializationSurrogate();
        }

        //Type is not marked (java.io.)Serializable
        selector = null;
        return null;
    }
}

public sealed class JavaSerializationSurrogate : ISerializationSurrogate {

    //Method called to serialize a java.io.Serializable object
    public void GetObjectData(Object obj, SerializationInfo info, StreamingContext context) {

        //Do the entire tree looking for the 'marker' methods
        var type = obj.GetType();
        while (type != null) 
        {
            var writeObject = type.GetMethod("writeObject", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(SerializationInfo), typeof(StreamingContext), typeof(Type) }, null );
            if (writeObject != null) {
                //Class has declared custom serialization so call through to that
                writeObject.Invoke(obj, new object[] { info, context, type });
            } else {
                //Default serialization of all non-transient fields at this level only (not the entire tree)
                obj.SerializeFields(info, context, type);
            }

            type = type.BaseType;   
        }
    }

    //Method called to deserialize a java.io.Serializable object
    public Object SetObjectData(Object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) {

        //Do the entire tree looking for the 'marker' methods
        var type = obj.GetType();
        while (type != null) 
        {
            var readObject = type.GetMethod("readObject", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(SerializationInfo), typeof(StreamingContext), typeof(Type) }, null );
            if (readObject != null) {
                //Class has declared custom serialization so call through to that
                readObject.Invoke(obj, new object[] { info, context, type });
            } else {
                //Default serialization of all non-transient fields at this level only (not the entire tree)
                obj.DeserializeFields(info, context, type);
            }

            type = type.BaseType;   
        }

        return null;
    }
}

[Serializable]
class A  : java.io.Serializable {
    public string Field1;
}

[Serializable]
class B : A {
    public string Field2; 

    private void readObject(SerializationInfo stream, StreamingContext context, Type declaringType) {
        stream.defaultReadObject(context, this, declaringType);

        Debug.WriteLine("B: readObject");
    } 

    private void writeObject(SerializationInfo stream, StreamingContext context, Type declaringType) {
        stream.defaultWriteObject(context, this, declaringType);

        Debug.WriteLine("B: writeObject");
    } 
}

[Serializable]
class C: B {
    public string Field3;

    private void writeObject(SerializationInfo stream, StreamingContext context, Type declaringType) {
        stream.defaultWriteObject(context, this, declaringType);

        Debug.WriteLine("C: writeObject");
    } 
}

public static class SerializationInfoExtensions {

    public static void defaultWriteObject(this SerializationInfo info, StreamingContext context, object o, Type declaringType) {
        o.SerializeFields(info, context, declaringType);
    }

    public static void defaultReadObject(this SerializationInfo info, StreamingContext context, object o, Type declaringType) {
        o.DeserializeFields(info, context, declaringType);
    }
}

class Program {
    static void Main(string[] args) {

        var myC = new C { Field1 = "tom", Field2 = "dick", Field3 = "harry" }; 

        using (var ms = new MemoryStream()) {
            var binaryFormatter = new BinaryFormatter();
            binaryFormatter.SurrogateSelector = new JavaSerializableSurrogateSelector();

            binaryFormatter.Serialize(ms, myC);
            ms.Position = 0;
            var myCDeserialized = binaryFormatter.Deserialize(ms);
        }
    }
}

/// <summary>
/// Extensions to the object class.
/// </summary>
public static class ObjectExtensions
{
    /// <summary>
    /// Serializes an object's class fields.
    /// </summary>
    /// <param name="source">The source object to serialize.</param>
    /// <param name="info">SerializationInfo.</param>
    /// <param name="context">StreamingContext.</param>
    /// <param name="declaringType">The level in the inheritance whose fields are to be serialized - pass null to serialize the entire tree.</param>
    public static void SerializeFields(this object source, SerializationInfo info, StreamingContext context, Type declaringType)
    {
        //Serialize the entire inheritance tree if there is no declaringType passed.
        var serializeTree = declaringType == null;

        //Set the level in the class heirarchy we are interested in - if there is no declaring type use the source type (and the entire tree will be done).
        var targetType = declaringType ?? source.GetType();

        //Get the set of serializable members for the target type
        var memberInfos = FormatterServices.GetSerializableMembers(targetType, context);

        // Serialize the base class's fields to the info object
        foreach (var mi in memberInfos)
        {
            if (serializeTree || mi.DeclaringType == targetType) {
                //Specify the name to use as the key - if the entire tree is being done then the names will already have a prefix. Otherwise, we need to 
                //append the name of the declaring type.
                var name = serializeTree ? mi.Name : mi.DeclaringType.Name + "$" + mi.Name;

                info.AddValue(name, ((FieldInfo)mi).GetValue(source));
            }
        }
    }

    /// <summary>
    /// Deserializes an object's fields.
    /// </summary>
    /// <param name="source">The source object to serialize.</param>
    /// <param name="info">SerializationInfo.</param>
    /// <param name="context">StreamingContext.</param>
    /// <param name="declaringType">The level in the inheritance whose fields are to be deserialized - pass null to deserialize the entire tree.</param>
    public static void DeserializeFields(this object source, SerializationInfo info, StreamingContext context, Type declaringType)
    {
        //Deserialize the entire inheritance tree if there is no declaringType passed.
        var deserializeTree = declaringType == null;

         //Set the level in the class heirarchy we are interested in - if there is no declaring type use the source type (and the entire tree will be done).
        var targetType = declaringType ?? source.GetType();

        var memberInfos = FormatterServices.GetSerializableMembers(targetType, context);

        // Deserialize the base class's fields from the info object
        foreach (var mi in memberInfos)
        {
            //Only serialize the fields at the specific level requested.
            if (deserializeTree || mi.DeclaringType == declaringType) 
            {
                // To ease coding, treat the member as a FieldInfo object
                var fi = (FieldInfo) mi;

                //Specify the name to use as the key - if the entire tree is being done then the names will already have a prefix. Otherwise, we need to 
                //append the name of the declaring type.
                var name = deserializeTree ? mi.Name : mi.DeclaringType.Name + "$" + mi.Name;

                // Set the field to the deserialized value
                fi.SetValue(source, info.GetValue(name, fi.FieldType));
            }
        }
    }
}
}
于 2012-12-29T00:01:12.663 に答える