构造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存取用的.其实可以直接采用这种方式来给对象进行处理.首先制定一个接口:
public interface IMessage { void Load(BufferReader reader); void Save(BufferWriter writer); }
实现以上接口实现一个简单自定义序列化对象功能
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); } }
把对象写入缓冲区
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);
从缓冲区读取对象
User nuser = new User(); BufferReader reader = new BufferReader(Encoding.UTF8, writer); nuser.Load(reader);
以上只是一个简单的对象,下面是构造一个相对复杂点的
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
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
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)
以上的实现也只是对之前文章提到的缓冲区使用,并没有用到池.对于如何使用池相信兴趣的同学来说没什么问题,需要的就从池中拿,用完就回收就是了.
访问Beetlex的Github