C# 二进制序列化、静态和单例

C# 二进制序列化、静态和单例,c#,serialization,static,singleton,binary-serialization,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 { [

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

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();
        }
    }    
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用System.IO;
使用System.Runtime.Serialization.Formatters.Binary;
命名空间staticorSingleContest
{
[系统可序列化]
公营武器
{
公共字符串名称;
公共武器(字符串名称)
{this.name=name;}
}
班级计划
{
公共静态武器剑=新武器(“剑”);
公共静态武器斧头=新武器(“斧头”);
静态void Main(字符串[]参数)
{
字节[]b;
字典WarriorSkills=新字典();
字典Des=新字典();
战斗技能。增加(剑,10);
使用(MemoryStream ms=new MemoryStream())
{
//连载
新的BinaryFormatter().Serialize(ms、WarriorSkills);
b=托雷女士();
//反序列化
弗拉什女士();
ms.Write(b,0,b.长度);
Seek女士(0,SeekOrigin.Begin);
Des=(字典)新的BinaryFormatter()。反序列化(ms);
}
Console.WriteLine(WarriorSkills.Keys.ToArray()[0].name+“是一个“+Des.Keys.ToArray()[0].name+”,但它们相等吗?+(WarriorSkills.Keys.ToArray()[0]==Des.Keys.ToArray()[0]).ToString();
Console.ReadLine();
Console.WriteLine(“战士的剑术是”,Des[剑]);//奇妙的“KeyNotFoundException”错误
Console.ReadLine();
}
}    
}
程序抛出错误,因为反序列化的“剑”不是同一个“剑”(它的
静态
,这是怎么发生的?)

制造
武器
a级
单体武器
不起作用,因为这样剑和斧头就成了一回事


有没有办法指出这两把剑是同一件事,或者我没有得到一些
静态类的核心逻辑?

也许解决这个问题的最好办法是根本避免序列化武器

相反,您可以序列化武器密钥,并使用类型安全枚举来查找武器

但是,在使用.NET二进制序列化作为持久机制之前,应该考虑其他所有选项——它执行得很差,并且有大量的GOTCHA会真正伤害您——特别是如果您试图保持版本向后兼容性。

[Serializable]
public sealed class Warrior
{
    private readonly Dictionary<int, int> weaponSkills = new Dictionary<int, int>();

    public void SetWeaponSkill(Weapon weapon, int skill)
    {
        weaponSkills[weapon.Key] = skill;
    }

    public int GetWeaponSkill(Weapon weapon)
    {
        int skill;
        if (!weaponSkills.TryGetValue(weapon.Key, out skill))
        {
            throw new ArgumentException("Warrior doesn't have weapon");
        }
        return skill;
    }
}

public static class Weapons
{
    public static readonly Weapon Sword = new Weapon(1, "Sword");

    public static readonly Weapon Dagger = new Weapon(2, "Dagger");
}

public sealed class Weapon
{
    public Weapon(int key, string text)
    {
        Key = key;
        Text = text;
    }

    public int Key { get; }
    public string Text { get; }
}
[可序列化]
公印级战士
{
私有只读字典weaponSkills=新字典();
公共无效设置武器技能(武器武器,智力技能)
{
武器技能[武器.钥匙]=技能;
}
公共智力获取武器技能(武器)
{
智力技能;
if(!weaponSkills.TryGetValue(weapon.Key,out skill))
{
抛出新的异常(“战士没有武器”);
}
回击技术;
}
}
公共静态类武器
{
公共静态只读武器剑=新武器(1,“剑”);
公共静态只读武器匕首=新武器(2,“匕首”);
}
公共密封类武器
{
公共武器(int键,字符串文本)
{
钥匙=钥匙;
文本=文本;
}
公共int密钥{get;}
公共字符串文本{get;}
}

也许解决这个问题的最好办法是根本避免序列化武器

相反,您可以序列化武器密钥,并使用类型安全枚举来查找武器

但是,在使用.NET二进制序列化作为持久机制之前,应该考虑其他所有选项——它执行得很差,并且有大量的GOTCHA会真正伤害您——特别是如果您试图保持版本向后兼容性。

[Serializable]
public sealed class Warrior
{
    private readonly Dictionary<int, int> weaponSkills = new Dictionary<int, int>();

    public void SetWeaponSkill(Weapon weapon, int skill)
    {
        weaponSkills[weapon.Key] = skill;
    }

    public int GetWeaponSkill(Weapon weapon)
    {
        int skill;
        if (!weaponSkills.TryGetValue(weapon.Key, out skill))
        {
            throw new ArgumentException("Warrior doesn't have weapon");
        }
        return skill;
    }
}

public static class Weapons
{
    public static readonly Weapon Sword = new Weapon(1, "Sword");

    public static readonly Weapon Dagger = new Weapon(2, "Dagger");
}

public sealed class Weapon
{
    public Weapon(int key, string text)
    {
        Key = key;
        Text = text;
    }

    public int Key { get; }
    public string Text { get; }
}
[可序列化]
公印级战士
{
私有只读字典weaponSkills=新字典();
公共无效设置武器技能(武器武器,智力技能)
{
武器技能[武器.钥匙]=技能;
}
公共智力获取武器技能(武器)
{
智力技能;
if(!weaponSkills.TryGetValue(weapon.Key,out skill))
{
抛出新的异常(“战士没有武器”);
}
回击技术;
}
}
公共静态类武器
{
公共静态只读武器剑=新武器(1,“剑”);
公共静态只读武器匕首=新武器(2,“匕首”);
}
公共密封类武器
{
公共武器(int键,字符串文本)
{
钥匙=钥匙;
文本=文本;
}
公共int密钥{get;}
公共字符串文本{get;}
}
如果反序列化(最初是单例)对象,它无论如何都将是一个新实例,除非指定反序列化应返回“已知”实例。但您可以通过一些定制来实现:

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
}
更新:

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

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);
}
如果反序列化一个(最初是单例的)对象,它无论如何都将是一个新实例,除非您指定反序列化应该返回一个“已知”实例。但您可以通过一些定制来实现:

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
}
更新:

如果你所有的武器属性都是常量(这不是问题