题目很简单, 就是 iMessage 对象怎么变成 Byte[]
答案 1:
msg.ToByteArray()
这肯定不符合我们的要求
答案 2:
- using var memoryStream = new MemoryStream();
- using var codedOutputStream = new CodedOutputStream(memoryStream);
- msg.WriteTo(codedOutputStream);
- codedOutputStream.Flush();
- memoryStream.ToArray();
这里面 memoryStream, codedOutputStream, 还有 ToArray 都产生了一个对象, MemoryStream 内部还会多产生一个 byte[] 对象
不符合要求
答案 3:
有人说你可以给 MemoryStream 传递一个 byte[] slice, 让 MemoryStream 直接用 byte[]
- var bytes = new byte[msg.CalculateSize()];
- using var memoryStream = new MemoryStream();
- using var codedOutputStream = new CodedOutputStream(memoryStream);
- msg.WriteTo(codedOutputStream);
- codedOutputStream.Flush();
这次消息直接被序列化到 bytes 里面去了, 但是 memoryStream 对象, codecOutputStream 还有 memoryStream 内部的 byte[] 都还在, 我就序列化了一个对象, 却产生了 3 个垃圾对象
所以, 来仔细看看 CodedOutputStream 类:
- /// <summary>
- /// Creates a new CodedOutputStream that writes directly to the given
- /// byte array. If more bytes are written than fit in the array,
- /// OutOfSpaceException will be thrown.
- /// </summary>
- public CodedOutputStream(byte[] flatArray) : this(flatArray, 0, flatArray.Length)
- {
- }
- /// <summary>
- /// Creates a new CodedOutputStream that writes directly to the given
- /// byte array slice. If more bytes are written than fit in the array,
- /// OutOfSpaceException will be thrown.
- /// </summary>
- private CodedOutputStream(byte[] buffer, int offset, int length)
- {
- this.output = null;
- this.buffer = buffer;
- this.position = offset;
- this.limit = offset + length;
- leaveOpen = true; // Simple way of avoiding trying to dispose of a null reference
- }
提供了一个 byte[] 的构造函数, 但是没提供 slice 的构造函数, 好在有一个私有的构造函数
答案 4:
这边就不写代码了, 大概意思就是通过反射私有构造函数来构造一个 CodedOutputStream 对象, 来省掉 MemoryStream 和他内部的 byte[]
现在离答案已经比较接近了
那我们的问题是, 能不能连 CodedOutputStream 也省掉呢?
答案 5 来了:
经过仔细观察, 发现这个类没有使用 Stream 的情况下, 就只需要修改 buffer, limit, 和 position 几个成员就行了, 虽然是 private 成员, 但是 C# 还是能修改
下来立马实践
- delegate void ClearCodedOutputStream(CodedOutputStream stream, byte[] buffer, int offset, int count);
- static ClearCodedOutputStream ResetCodedOutputStream;
- static CodedOutputStream codedOutputStream = new CodedOutputStream(new byte[10]);
- static unsafe void Encode(iMessage msg, byte[] buffer)
- {
- ResetCodedOutputStream(codedOutputStream, buffer, 0, buffer.Length);
- msg.WriteTo(codedOutputStream);
- codedOutputStream.Flush();
- }
- static Action<T, TValue> MakeSetter<T, TValue>(FieldInfo field)
- {
- DynamicMethod m = new DynamicMethod(
- "setter", typeof(void), new Type[] { typeof(T), typeof(TValue) }, typeof(Program));
- ILGenerator cg = m.GetILGenerator();
- cg.Emit(OpCodes.Ldarg_0);
- cg.Emit(OpCodes.Ldarg_1);
- cg.Emit(OpCodes.Stfld, field);
- cg.Emit(OpCodes.Ret);
- return (Action<T, TValue>)m.CreateDelegate(typeof(Action<T, TValue>));
- }
- static void Main(string[] args)
- {
- var bufferField = typeof(CodedOutputStream).GetField("buffer", BindingFlags.NonPublic | BindingFlags.Instance);
- var limitField = typeof(CodedOutputStream).GetField("limit", BindingFlags.NonPublic | BindingFlags.Instance);
- var positionField = typeof(CodedOutputStream).GetField("position", BindingFlags.NonPublic | BindingFlags.Instance);
- var setLimit = MakeSetter<CodedOutputStream, int>(limitField);
- var setPosition = MakeSetter<CodedOutputStream, int>(positionField);
- var setBuffer = MakeSetter<CodedOutputStream, byte[]>(bufferField);
- ResetCodedOutputStream = (stream, buffer, offset, length) =>
- {
- //this.buffer = buffer;
- //this.position = offset;
- //this.limit = offset + length;
- setBuffer(stream, buffer);
- setPosition(stream, offset);
- setLimit(stream, offset + length);
- };
- var buffer = new byte[msg.CalculateSize()];
- Encode(msg, buffer);
- }
这个实例代码里面, 用了一个 static 的全局 CodedOutputStream, 真正用的时候, 肯定要保证线程安全.
所以接下来的问题是:
1. 如何保证 CodedOutputStream 对象线程安全
2. 如何把 var buffer = new byte[msg.CalculateSize()]; 这个也省掉
这俩问题就留给读者思考.
来源: https://www.cnblogs.com/egmkang/p/12635247.html