构造BufferWriter和BufferReader实现高效的对象序列化和反序列化

    在之前的文章讲术了如何设计一个简单的网络缓冲区和缓冲池,在.net网络应用中有一个普遍的问题就是如何把对象写入缓冲和从缓冲中读取数据还原对象.在这里会实现一个Writer和Reader方便地把信息写入缓冲区和从缓冲区读取;先看下Writer和Reader功能.

BufferWriter

 

复制代码
View Code
public class BufferWriter : IDisposable
{
private IList<Buffer> mBuffers = new List<Buffer>(20);
public IList<Buffer> Buffers
{
get
{
return mBuffers;
}
}
public BufferWriter(System.Text.Encoding coding)
{
Coding = coding;


}
public Encoding Coding
{
get;
set;
}
public void Dispose()
{
foreach (Buffer item in mBuffers)
{
item.Dispose();
}
mBuffers.Clear();
}
public void Write(byte value)
{
Write(new byte[] { value });
}
public void Write(DateTime datetime)
{
Write(BitConverter.GetBytes(datetime.Ticks));
}
private bool WriteArrayLength(Array value)
{
if (value == null)
{
Write(BitConverter.GetBytes(0));

}
else
{
Write(BitConverter.GetBytes(value.Length));
}
return value != null && value.Length > 0;
}
public void WriteDataTimeArray(DateTime[] value)
{
if (WriteArrayLength(value))
{
for (int i = 0; i < value.Length; i++)
{
Write(value[i]);
}
}
}
public void Write(float value)
{
Write(BitConverter.GetBytes(value));
}
public void WriteFloatArray(float[] value)
{
if (WriteArrayLength(value))
{
for (int i = 0; i < value.Length; i++)
{
Write(value[i]);
}
}
}
public void Write(bool value)
{
Write(BitConverter.GetBytes(value));
}
public void WriteBoolArray(bool[] value)
{
if (WriteArrayLength(value))
{
for (int i = 0; i < value.Length; i++)
{
Write(value[i]);
}
}
}
public void Write(char value)
{
Write(BitConverter.GetBytes(value));
}
public void WriteCharArray(char[] value)
{
if (WriteArrayLength(value))
{
for (int i = 0; i < value.Length; i++)
{
Write(value[i]);
}
}
}
public void Write(double value)
{
Write(BitConverter.GetBytes(value));
}
public void WriteDoubleArray(double[] value)
{
if (WriteArrayLength(value))
{
for (int i = 0; i < value.Length; i++)
{
Write(value[i]);
}
}
}
public void Write(Int16 value)
{
Write(BitConverter.GetBytes(value));
}
public void WriteInt16Array(Int16[] value)
{
if (WriteArrayLength(value))
{
for (int i = 0; i < value.Length; i++)
{
Write(value[i]);
}
}
}
public void Write(Int32 value)
{
Write(BitConverter.GetBytes(value));
}
public void WriteInt32Array(Int32[] value)
{
if (WriteArrayLength(value))
{
for (int i = 0; i < value.Length; i++)
{
Write(value[i]);
}
}
}
public void Write(Int64 value)
{
Write(BitConverter.GetBytes(value));
}
public void WriteInt64Array(Int64[] value)
{
if (WriteArrayLength(value))
{
for (int i = 0; i < value.Length; i++)
{
Write(value[i]);
}
}
}
public void Write(string value)
{
Write(Coding.GetBytes(value));
}
public void WriteBlock(string value)
{
if (string.IsNullOrEmpty(value))
{
Write(0);

}
else
{
byte[] data = Coding.GetBytes(value);
Write(data.Length);
Write(data);
}
}
public void WriteBlockArray(string[] value)
{
if (WriteArrayLength(value))
{
for (int i = 0; i < value.Length; i++)
{
WriteBlock (value[i]);
}
}
}
public void WriteObject(IMessage obj)
{
obj.Save(this);
}
public void WriteObjects<T>(IList<T> objs) where T:IMessage
{
if (objs == null)
{
Write(0);
return;
}
Write(objs.Count);
for (int i = 0; i < objs.Count; i++)
{
WriteObject(objs[i]);
}

}
public void Write(byte[] value)
{
mBuffers.Add(new Buffer(value));

}
}
复制代码

 

BufferReader

复制代码
View Code
public class BufferReader : IDisposable
{
private IList<Buffer> mBuffers;
private int mIndex = 0;
private Buffer mCursorBuffer;
public BufferReader(Encoding coding, IList<Buffer> buffers)
{
mBuffers = buffers;
Coding = coding;
foreach (Buffer item in mBuffers)
{
item.Reset();
}
nextBuffer();
}
public BufferReader(Encoding coding, BufferWriter write)
{
Coding = coding;
mBuffers = write.Buffers;
foreach (Buffer item in mBuffers)
{
item.Reset();
}
nextBuffer();
}
public Encoding Coding
{
get;
set;
}
private void nextBuffer()
{
mCursorBuffer = mBuffers[mIndex];
mIndex++;
}
public ArraySegment<byte> Read(int count)
{

ArraySegment<byte> result = mCursorBuffer.Read(count);
if (result.Count == count)
return result;
int readcount = 0;
byte[] data = new byte[count];
while (true)
{

Array.Copy(result.Array, result.Offset, data, readcount, result.Count);
readcount += result.Count;
if (readcount >= count)
break;
nextBuffer();
result = mCursorBuffer.Read(count - readcount);
}
return new ArraySegment<byte>(data, 0, count);

}
private T[] createArray<T>()
{
T[] result = new T[ReadInt32()];
return result;

}
public bool ReadBool()
{
ArraySegment<byte> data = Read(1);
bool result = BitConverter.ToBoolean(data.Array, data.Offset);
return result;
}
public bool[] ReadBoolArray()
{
bool[] result = createArray<bool>();
for (int i = 0; i < result.Length; i++)
{
result[i] = ReadBool();
}
return result;
}
public char ReadChar()
{
ArraySegment<byte> data = Read(2);
char result = BitConverter.ToChar(data.Array, data.Offset);

return result;
}
public char[] ReadCharArray()
{
char[] result = createArray<char>();
for (int i = 0; i < result.Length; i++)
{
result[i] = ReadChar();
}
return result;
}
public double ReadDouble()
{
ArraySegment<byte> data = Read(8);
double result = BitConverter.ToDouble(data.Array, data.Offset);

return result;
}
public double[] ReadDoubleArray()
{
double[] result = createArray<double>();
for (int i = 0; i < result.Length; i++)
{
result[i]=ReadDouble();
}
return result;
}
public Int16 ReadInt16()
{
ArraySegment<byte> data = Read(2);
Int16 result = BitConverter.ToInt16(data.Array, data.Offset);

return result;
}
public Int16[] ReadInt16Array()
{
Int16[] result = createArray<Int16>();
for (int i = 0; i < result.Length; i++)
{
result[i] = ReadInt16();
}
return result;
}
public Int32 ReadInt32()
{
ArraySegment<byte> data = Read(4);
Int32 result = BitConverter.ToInt32(data.Array, data.Offset);

return result;
}
public Int32[] ReadInt32Array()
{
Int32[] result = createArray<Int32>();
for (int i = 0; i < result.Length; i++)
{
result[i] = ReadInt32();
}
return result;
}
public Int64 ReadInt64()
{
ArraySegment<byte> data = Read(8);
Int64 result = BitConverter.ToInt64(data.Array, data.Offset);

return result;
}
public Int64[] ReadInt64Array()
{
Int64[] result = createArray<Int64>();
for (int i = 0; i < result.Length; i++)
{
result[i] = ReadInt64();
}
return result;
}
public float ReadFloat()
{
ArraySegment<byte> data = Read(4);
Single result = BitConverter.ToSingle(data.Array, data.Offset);

return result;
}
public float[] ReadFloatArray()
{
float[] result = createArray<float>();
for (int i = 0; i < result.Length; i++)
{
result[i] = ReadFloat();
}
return result;
}
public DateTime ReadDateTime()
{
long date = ReadInt64();
return new DateTime(date);
}
public DateTime[] ReadDateTimeArray()
{
DateTime[] result = createArray<DateTime>();
for (int i = 0; i < result.Length; i++)
{
result[i] = ReadDateTime();
}
return result;
}
public string ReadBlock()
{
int length = ReadInt32();
if (length > 0)
return ReadString(length);
return null;
}
public string[] ReadBlockArray()
{
string[] result = createArray<string>();
for (int i = 0; i < result.Length; i++)
{
result[i] = ReadBlock();
}
return result;
}
public T ReadObject<T>() where T : IMessage,new()
{
T item = new T();
item.Load(this);
return item;
}
public IList<T> ReadObjects<T>() where T : IMessage, new()
{
List<T> items = new List<T>();
int count = ReadInt32();
for (int i = 0; i < count; i++)
{
items.Add(ReadObject<T>());
}
return items;

}
public string ReadString(int count)
{
ArraySegment<byte> data = Read(count);
string result = Coding.GetString(data.Array, data.Offset, count);
return result;
}
public void Dispose()
{
foreach (Buffer item in mBuffers)
{
item.Dispose();
}
mBuffers.Clear();
}
}
复制代码

 

以上两个对象的方法都是基于BitConverter进行基础类型的处理,即方便又高效;但这样会存在一个问题就是不能处理复杂的对象,对于复杂的对象可以采用规则约束的方式来处理;如果对asp.net的ViewState有了解的同学们应该知道有一个IStateManager接口用于对象自定义ViewState存取用的.其实可以直接采用这种方式来给对象进行处理.首先制定一个接口:

1
2
3
4
5
public interface IMessage
    {
        void Load(BufferReader reader);
        void Save(BufferWriter writer);
    }

实现以上接口实现一个简单自定义序列化对象功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class User : IMessage
        {
            public User()
            {
                Accunts = new List<Account>();
            }
            public string Name
            {
                get;
                set;
            }
            public string EMail
            {
                get;
                set;
            }
            public string City
            {
                get;
                set;
            }
            public DateTime Birthdate
            {
                get;
                set;
            }
            public void Load(BufferReader reader)
            {
                Name = reader.ReadBlock();
                EMail = reader.ReadBlock();
                City = reader.ReadBlock();
                Birthdate = reader.ReadDateTime();
                Accunts = reader.ReadObjects<Account>();
            }
            public void Save(BufferWriter writer)
            {
                writer.WriteBlock(Name);
                writer.WriteBlock(EMail);
                writer.WriteBlock(City);
                writer.Write(Birthdate);
                writer.WriteObjects<Account>(Accunts);
                
            }
}

把对象写入缓冲区 

1
2
3
4
5
6
7
User user = new User();
 user.Name = "henryfan";
 user.City = "GuangZhou";
 user.EMail = "henryfan@msn.com";
 user.Birthdate = DateTime.Now;
 BufferWriter writer = new BufferWriter(Encoding.UTF8);
 user.Save(writer);

从缓冲区读取对象

1
2
3
User nuser = new User();
 BufferReader reader = new BufferReader(Encoding.UTF8, writer);
 nuser.Load(reader);

以上只是一个简单的对象,下面是构造一个相对复杂点的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
public class User : IMessage
        {
            public User()
            {
                Accunts = new List<Account>();
            }
            public string Name
            {
                get;
                set;
            }
            public string EMail
            {
                get;
                set;
            }
            public string City
            {
                get;
                set;
            }
            public DateTime Birthdate
            {
                get;
                set;
            }
            public void Load(BufferReader reader)
            {
                Name = reader.ReadBlock();
                EMail = reader.ReadBlock();
                City = reader.ReadBlock();
                Birthdate = reader.ReadDateTime();
                Accunts = reader.ReadObjects<Account>();
            }
            public void Save(BufferWriter writer)
            {
                writer.WriteBlock(Name);
                writer.WriteBlock(EMail);
                writer.WriteBlock(City);
                writer.Write(Birthdate);
                writer.WriteObjects<Account>(Accunts);
                
            }
            public IList<Account> Accunts
            {
                get;
                set;
            }
            public class Account:IMessage
            {
                public Account()
                {
                    Items = new List<History>();
                }
                public string ID { get; set; }
                public double Money { get; set; }
 
                public IList<History> Items
                {
                    get;
                    set;
                }
                public void Load(BufferReader reader)
                {
                    ID = reader.ReadBlock();
                    Money = reader.ReadDouble();
                    Items = reader.ReadObjects<History>();
                }
 
                public void Save(BufferWriter writer)
                {
                    writer.WriteBlock(ID);
                    writer.Write(Money);
                    writer.WriteObjects<History>(Items);
                }
            }
            public class History:IMessage
            {
                public string ID
                {
                    get;
                    set;
                }
                public string Type
                {
                    get;
                    set;
                }
                public double Money
                {
                    get;
                    set;
                }
                public DateTime LastChangeDate
                {
                    get;
                    set;
                }
 
 
                public void Load(BufferReader reader)
                {
                    ID = reader.ReadBlock();
                    Type = reader.ReadBlock();
                    Money = reader.ReadDouble();
                    LastChangeDate = reader.ReadDateTime();
                }
 
                public void Save(BufferWriter writer)
                {
                    writer.WriteBlock(ID.ToString());
                    writer.WriteBlock(Type);
                    writer.Write(Money);
                    writer.Write(LastChangeDate);
                }
            }
        }

简单性能测试

以下在一台I5的电脑上做简单的性能测试,编译Release后执行相关程序

测试1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static void test1()
{
    User user = new User();
    user.Name = "henryfan";
    user.City = "GuangZhou";
    user.EMail = "henryfan@msn.com";
    user.Birthdate = DateTime.Now;
   
 
    for (int i = 0; i < 500000; i++)
    {
 
        BufferWriter writer = new BufferWriter(Encoding.UTF8);
        user.Save(writer);
        User nuser = new User();
        BufferReader reader = new BufferReader(Encoding.UTF8, writer);
        nuser.Load(reader);
    }
}

耗时:976.7241(ms)

测试2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static void test()
{
    User user = new User();
    user.Name = "henryfan";
    user.City = "GuangZhou";
    user.EMail = "henryfan@msn.com";
    user.Birthdate = DateTime.Now;
    user.Accunts.Add(new User.Account { ID = "ac1", Money = 5 });
    user.Accunts[0].Items.Add(new User.History { ID = Guid.NewGuid().ToString(), LastChangeDate = DateTime.Now, Money = 50, Type = "BBQ" });
    user.Accunts[0].Items.Add(new User.History { ID = Guid.NewGuid().ToString(), LastChangeDate = DateTime.Now, Money = 34, Type = "BBE" });
    user.Accunts[0].Items.Add(new User.History { ID = Guid.NewGuid().ToString(), LastChangeDate = DateTime.Now, Money = 44, Type = "DDEQ" });
     
    for (int i = 0; i <500000; i++)
    {
         
        BufferWriter writer = new BufferWriter(Encoding.UTF8);
        user.Save(writer);
        User nuser = new User();
        BufferReader reader = new BufferReader(Encoding.UTF8, writer);
        nuser.Load(reader);
    }
}

耗时:4119.8927(ms)

以上的实现也只是对之前文章提到的缓冲区使用,并没有用到池.对于如何使用池相信兴趣的同学来说没什么问题,需要的就从池中拿,用完就回收就是了.

posted @   beetlex  阅读(4239)  评论(3编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
点击右上角即可分享
微信分享提示