none
一个设计模式的问题 RRS feed

  • 问题

  • 我最近做的一个小游戏项目中,由于涉及到一个保存的问题,我需要将对象序列化,于是我想到了下列模式:

    实例方法:byte[] T.ToBytes();
    静态方法:T T.FromBytes(byte[] bytes, ref int offset);

    这样有一个问题,静态方法不能实现为接口,实际上我都没能实现为接口,而当我涉及到更加复杂的情况,如List<T>时,便遇到一些问题了:

    internal static byte[] ToBytes<T>(this List<T> value)
    {
      var bytes = new List<byte>();
      bytes.AddRange(BitConverter.GetBytes(value.Count)
      foreach (dynamic t in value)
      {
        bytes.AddRange(t.ToBytes());
      }
      return bytes.ToArray();
    }
    
    

    由于没有实现为接口,我不得不使用dynamic,总的感觉很一般,但是对于FromBytes,问题就严重了:

    internal static List<T> FromBytes<T>(this List<T> value, byte[] bytes, ref int startIndex)
    {
      var count = BitConverter.ToInt32(bytes, startIndex);
      var result = new List<T>(count);
      for (var i = 0; i < count; i++)
      {
        result.Add(
          T.FromBytes(bytes, ref startIndex));
      }
    }
    
    由于静态方法不能实现为接口,所以以上代码不能通过编译,请问在这种情况下我应该如何进行设计?

    2011年6月20日 8:33

答案

  • 首先,您也许可以考虑把 ToBytes 的适用范围扩大,从 List<T> 扩展到 Object。因为,从设计理念上,您的每一个对象应该都能够被 ToBytes 或者每个对象实例都可以被 FromBytes 创建。

    所以,这需要设计类似于下面的代码,问题就可以轻松解决了。注意,理论上不应该把 FromBytes 实现为扩展方法,因为扩展方法表现为实例方法,而 FromBytes 更像是一个静态方法。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.IO;

    namespace Demo
    {
        class Program
        {
            static void Main(string[] args)
            {
                IEnumerable<byte> data = (new List<string> { "1""2" }).ToBytes();

                List<string> list = BinarySerializationExtentions.FromBytes<List<string>>(data);

                Console.WriteLine(data.Count());
                Console.WriteLine(list.Count);
            }
        }

        public static class BinarySerializationExtentions
        {
            public static IEnumerable<byte> ToBytes(this object source)
            {
                if (source == null)
                {
                    throw new ArgumentNullException("source");
                }

                BinaryFormatter formatter = new BinaryFormatter();

                using (MemoryStream stream = new MemoryStream())
                {
                    formatter.Serialize(stream, source);
                    stream.Position = 0;

                    using (BinaryReader reader = new BinaryReader(stream))
                    {
                        return reader.ReadBytes((int)reader.BaseStream.Length);
                    }
                }
            }

            public static T FromBytes<T>(IEnumerable<byte> bytes)
            {
                if (bytes == null)
                {
                    throw new ArgumentNullException("bytes");
                }

                BinaryFormatter formatter = new BinaryFormatter();
                
                using (MemoryStream stream = new MemoryStream (bytes.ToArray()))
                {
                    return (T)formatter.Deserialize(stream);
                }
            }
        }
    }

    Mark Zhou
    • 已标记为答案 Flysha 2011年6月20日 13:13
    2011年6月20日 9:30

全部回复

  • 首先,您也许可以考虑把 ToBytes 的适用范围扩大,从 List<T> 扩展到 Object。因为,从设计理念上,您的每一个对象应该都能够被 ToBytes 或者每个对象实例都可以被 FromBytes 创建。

    所以,这需要设计类似于下面的代码,问题就可以轻松解决了。注意,理论上不应该把 FromBytes 实现为扩展方法,因为扩展方法表现为实例方法,而 FromBytes 更像是一个静态方法。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.IO;

    namespace Demo
    {
        class Program
        {
            static void Main(string[] args)
            {
                IEnumerable<byte> data = (new List<string> { "1""2" }).ToBytes();

                List<string> list = BinarySerializationExtentions.FromBytes<List<string>>(data);

                Console.WriteLine(data.Count());
                Console.WriteLine(list.Count);
            }
        }

        public static class BinarySerializationExtentions
        {
            public static IEnumerable<byte> ToBytes(this object source)
            {
                if (source == null)
                {
                    throw new ArgumentNullException("source");
                }

                BinaryFormatter formatter = new BinaryFormatter();

                using (MemoryStream stream = new MemoryStream())
                {
                    formatter.Serialize(stream, source);
                    stream.Position = 0;

                    using (BinaryReader reader = new BinaryReader(stream))
                    {
                        return reader.ReadBytes((int)reader.BaseStream.Length);
                    }
                }
            }

            public static T FromBytes<T>(IEnumerable<byte> bytes)
            {
                if (bytes == null)
                {
                    throw new ArgumentNullException("bytes");
                }

                BinaryFormatter formatter = new BinaryFormatter();
                
                using (MemoryStream stream = new MemoryStream (bytes.ToArray()))
                {
                    return (T)formatter.Deserialize(stream);
                }
            }
        }
    }

    Mark Zhou
    • 已标记为答案 Flysha 2011年6月20日 13:13
    2011年6月20日 9:30
  • 谢谢,回答得很详细,从你的答复中我学到了很多!

    特别是System.Runtime.Serialization.Formatters.Binary名字空间的一些调用,真是非常妙!

    2011年6月20日 13:14