2012-12-28 6 views
5

Ich konvertiere ein Java-Spiel in C# (Titan Attacks von Puppy Games) und bin ziemlich viel jetzt abgesehen von der letzten Aufgabe, die Serialisierung des Spiels Zustand ist für Sicherungsdateien..Net Serialisierung - Mischen [Serializable] mit benutzerdefinierten in Vererbungsbaum

Typische Hierarchie: Ressource (Basis) -> Feature-> Screen/Wirkung/Entity-> GameScreen/LaserEffect/Invader

der Java-Code verwendet Standard Objectoutput/Object binäre Serialisierung durchzuführen, aber annoyingly führt einige readResolve/writeResolve arbeitet auf der Basisklassenebene (Resource), um den Serialisierungsprozess anzupassen (wenn eine Ressource benannt wird, wird sie nicht serialisiert und gibt einfach einen Proxy mit einem Namen zurück, der später zum Abrufen der Ressource aus einer Hashmap verwendet wird).

Meine naiven Lösungen sind blind, diesen Ansatz zu kopieren und ISerializable in der Basisklasse implementieren die 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 außer Kraft zu setzen) Also, ich bin ziemlich sicher, dass alle Wetten ausgeschaltet sind für gebaut -in Serialisierung und ich muss ISerializable die Erbschaftskette zusammen mit dem Serialisierungskonstruktor vollständig implementieren?

Hinweis GetObjectData ist virtuell, daher können abgeleitete Klassen ihre Felder serialisieren und dann die Basisklasse aufrufen. Das funktioniert, aber es ist eine Menge langweiliger Arbeit, die zu vielen Klassen (100s) hinzukommt.

Einige abgeleitete Typen (Sprite, InvaderBehaviour, usw.) führen auch benutzerdefinierte Serialisierungsarbeiten durch, um die Sache noch schlimmer zu machen.

Ich habe Jeffrey Richters Artikel zu diesem Thema angeschaut und versucht, ein ResourceSurrogateSelector: ISerializationSurrogate-Typ-Konstrukt zu verwenden, aber diese Serialisierungsmethoden werden nur aufgerufen, wenn der serialisierte Typ eine Ressource und kein von Ressource abgeleiteter Typ ist nicht aufgerufen werden Serialisierung eines Invader oder GameScreen)

Q) gibt es eine kluge Möglichkeit, dies zu tun?

Ich habe es geschafft, die beiden Code-Basen ziemlich nahe beieinander zu halten und das hat die Konvertierung viel einfacher gemacht - ich würde diesen Ansatz hier fortsetzen (also kein XmlSerializer, Protobuf, etc), es sei denn wirklich zwingenden Grund nicht zu.

Ich habe darüber nachgedacht, Java zu schreiben, um den Prozess zu automatisieren und die Typen zu implementieren, die die Serializable-Schnittstelle implementieren und eine .cs-Datei mit dem gesamten .Net-Serialisierungscode erstellen, damit ich die Hauptklassendateien nicht verschmutze (Ich würde sie teilweise machen)

PS - Zielplattformen werden Windows8/Surface/XBox360 auf der .Net-Seite der Dinge sein (so Version 4) und wahrscheinlich PS Vita/vielleicht iOS mit Mono. Saves werden auf der Plattform, auf der sie serialisiert wurden, deserialisiert.

EDIT Eine Antwort von Sergey Teplyakov in diesem Beitrag .... .NET, C#: How to add a custom serialization attribute that acts as ISerializable interface ... hat mich zu dem ISurrogateSelector Schnittstelle geführt, wie es den gewünschten abgeleiteten Klassen mit der Auswahl helfen aussieht.

Antwort

1

Das ist, was ich bis jetzt geschafft habe und ich bin ziemlich glücklich damit :-) Nur noch zu lesenResolve/writeReplace und ich bin fertig! (Ich werde wahrscheinlich auch die Object-, SerializationInfo- und StreamingContext-Argumente in einem ObjectOutputStream für ein gutes Maß einpacken).

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)); 
      } 
     } 
    } 
} 
}