BinarySerialization,Static和Singleton

时间:2018-01-17 14:08:16

标签: c# serialization static singleton binary-serialization

让我的类经历二进制序列化之后,对另一个类的静态实例的任何引用都会中断。 这个例子应该更好地解释我的意思:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

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

namespace staticorsingletontest
{
    [System.Serializable]
    public class weapon 
    {
        public string name;
        public weapon (string name)
            { this.name = name; }
    }
    class Program
    {
        public static weapon sword =  new weapon("Sword");
        public static weapon axe = new weapon("Axe");
        static void Main(string[] args)
        {

            byte[] b;
            Dictionary<weapon, int> WarriorSkills = new Dictionary<weapon,int>();
            Dictionary<weapon, int> Des = new Dictionary<weapon,int>();

            WarriorSkills.Add(sword, 10);
            using (MemoryStream ms = new MemoryStream())
            {
                //Serialize
                new BinaryFormatter().Serialize(ms, WarriorSkills);
                b = ms.ToArray();
                //Deserialize
                ms.Flush();
                ms.Write(b, 0, b.Length);
                ms.Seek(0, SeekOrigin.Begin);

                Des = (Dictionary<weapon, int>)new BinaryFormatter().Deserialize(ms);
            }

            Console.WriteLine(WarriorSkills.Keys.ToArray()[0].name + " is a " + Des.Keys.ToArray()[0].name + ", but are they equal? " + (WarriorSkills.Keys.ToArray()[0] == Des.Keys.ToArray()[0]).ToString());

            Console.ReadLine();

            Console.WriteLine("Warrior's Skill with Sword is ", Des[sword]); //wonderful "KeyNotFoundException" error

            Console.ReadLine();
        }
    }    
}

程序抛出一个错误,因为反序列化的“剑”不是同一个“剑”(它的static,甚至会发生什么?)

weapon成为一个singleton类是行不通的,因为剑与斧将是同一回事。

有没有办法指出两把剑是相同的,或者我没有得到static类的核心逻辑?

2 个答案:

答案 0 :(得分:1)

也许最好的方法是避免序列化武器。

您可以改为序列化武器钥匙,并使用类型安全枚举来查找武器。

但是,在使用.net二进制序列化作为持久性机制之前,你应该考虑其他所有选项 - 它执行得非常糟糕,并且有很多陷阱真的会伤害你 - 特别是如果你试图维护版本向后兼容性。

SELECT  REGEXP_REPLACE ('SEPA1,30-NOV-17;SEPA2,30-NOV-17;SEPA3,30-NOV-17;',
                        '([^,]+)(\1)+', '')
FROM dual;

答案 1 :(得分:1)

如果你反序列化一个(原来的单例)对象,它将是一个新的实例,除非你指定反序列化应该返回一个&#34;众所周知的&#34;实例。但你可以通过一些定制来做到这一点:

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

public class Example
{
    [Serializable]
    public class Weapon: IObjectReference // here is the trick, see GetRealObject method
    {
        // unless you want to allow to create any kind of weapon I suggest to use an enum for the predefined types
        private enum WeaponKind { Sword, Axe }

        public static Weapon Sword { get; } = new Weapon(WeaponKind.Sword);
        public static Weapon Axe { get; } = new Weapon(WeaponKind.Axe);

        // this is the only instance field so this will be stored on serialization
        private readonly WeaponKind kind;

        public string Name => kind.ToString();

        // make the constructor private so no one can create further weapons
        private Weapon(WeaponKind kind)
        {
            this.kind = kind;
        }

        // on deserialization ALWAYS a new instance will be created
        // but if you implement IObjectReference, this method will be called before returning the deserialized object
        public object GetRealObject(StreamingContext context)
        {
            // map the temporarily created new deserialized instance to the well-known static member:
            switch (kind)
            {
                case WeaponKind.Sword:
                    return Sword;
                case WeaponKind.Axe:
                    return Axe;
                default:
                    throw new InvalidOperationException("Unknown weapon type");
            }
        }
    }
}

还有一些测试:

public static void Main()
{
    var axe = Weapon.Axe;
    var savedContent = new MemoryStream();
    var formatter = new BinaryFormatter();
    formatter.Serialize(savedContent, axe);
    savedContent.Position = 0;
    var deserializedAxe = (Weapon)formatter.Deserialize(savedContent);
    Console.WriteLine(ReferenceEquals(axe, deserializedAxe)); // prints True
}

<强>更新

如果您的所有武器属性都是常量(如果有更多实例应该被视为相等则不是问题),那么只需覆盖Equals

public override bool Equals(object obj)
{
    var other = obj as Weapon;
    if (other == null)
        return base.Equals(obj);
    return other.kind == this.kind;
}

如果覆盖Equals,则必须覆盖GetHashCode,否则,您将无法在字典中找到同一对象的不同实例:

public override int GetHashCode()
{
    return kind.GetHashCode();
}

请注意,==运算符仍会返回引用相等性。如果要覆盖此设置,则需要重载==!=运算符:

public static bool operator ==(Weapon w1, Weapon w2)
{
    return Equals(w1, w2);
}

public static bool operator !=(Weapon w1, Weapon w2)
{
    return !Equals(w1, w2);
}