C# WinRT和从字节数组到字节数组的持久化结构?
通过使用.NET4.0,我可以通过使用封送处理类,快速地将结构转换为字节数组或从字节数组转换为字节数组。例如,下面的简单示例将在我的机器上以每秒100万次的速度运行,这对于我的目的来说已经足够快了C# WinRT和从字节数组到字节数组的持久化结构?,c#,.net,windows-runtime,microsoft-metro,C#,.net,Windows Runtime,Microsoft Metro,通过使用.NET4.0,我可以通过使用封送处理类,快速地将结构转换为字节数组或从字节数组转换为字节数组。例如,下面的简单示例将在我的机器上以每秒100万次的速度运行,这对于我的目的来说已经足够快了 [StructLayout(LayoutKind.Sequential)] public struct ExampleStruct { int i1; int i2; } public byte[] StructToBytes(
[StructLayout(LayoutKind.Sequential)]
public struct ExampleStruct
{
int i1;
int i2;
}
public byte[] StructToBytes()
{
ExampleStruct inst = new ExampleStruct();
int len = Marshal.SizeOf(inst);
byte[] arr = new byte[len];
IntPtr ptr = Marshal.AllocHGlobal(len);
Marshal.StructureToPtr(inst, ptr, true);
Marshal.Copy(ptr, arr, 0, len);
Marshal.FreeHGlobal(ptr);
return arr;
}
但是,封送处理类在WinRT下不可用,出于安全原因,这已经足够合理了,但这意味着我需要另一种方法来实现从字节数组到结构的转换
我正在寻找一种适用于任何固定大小结构的方法。我可以通过为每个知道如何将特定结构转换为字节数组并形成字节数组的结构编写自定义代码来解决这个问题,但这相当繁琐,我忍不住觉得有一些通用的解决方案。一种方法是将表达式和反射相结合(我将把缓存作为实现细节):
//将每个字段写入BinaryWriter的给定结构的操作
静态操作CreateWriter()
{
//TODO:cache/validate T是一个“简单”结构
var bw=Expression.Parameter(typeof(BinaryWriter),“bw”);
var obj=表达式参数(类型(T),“值”);
//我无法确定.Net for Metro是否有BlockExpression
//如果没有,则需要一个返回虚拟值的垫片
//用加法或布尔运算组合
var body=Expression.Block(
从typeof(T.GetTypeInfo().DeclaredFields中的f开始
选择表达式。调用(
bw,
“写”,
Type.EmptyTypes,//不是泛型方法
新[]{Expression.Field(obj,f.Name)});
var action=Expression.Lambda(
身体,
新[]{bw,obj});
返回action.Compile();
}
这样使用:
public static byte[] GetBytes<T>(T value)
{
// TODO: validation and caching as necessary
var writer = CreateWriter(value);
var memory = new MemoryStream();
writer(new BinaryWriter(memory), value);
return memory.ToArray();
}
publicstaticbyte[]GetBytes(T值)
{
//TODO:根据需要进行验证和缓存
var writer=CreateWriter(值);
var memory=newmemoryStream();
writer(新的二进制writer(内存),值);
返回内存。ToArray();
}
回过头来看,它更为复杂:
static MethodInfo[] readers = typeof(BinaryReader).GetTypeInfo()
.DeclaredMethods
.Where(m => m.Name.StartsWith("Read") && !m.GetParameters().Any())
.ToArray();
// Action for a given struct that reads each field from a BinaryReader
static Func<BinaryReader, T> CreateReader<T>()
{
// TODO: cache/validate T is a "simple" struct
var br = Expression.Parameter(typeof(BinaryReader), "br");
var info = typeof(T).GetTypeInfo();
var body = Expression.MemberInit(
Expression.New(typeof(T)),
from f in info.DeclaredFields
select Expression.Bind(
f,
Expression.Call(
br,
readers.Single(m => m.ReturnType == f.FieldType),
Type.EmptyTypes, // Not a generic method
new Expression[0]));
var function = Expression.Lambda<Func<BinaryReader, T>>(
body,
new[] { br });
return function.Compile();
}
static MethodInfo[]readers=typeof(BinaryReader).GetTypeInfo()
.公布方法
.Where(m=>m.Name.StartsWith(“Read”)&&!m.GetParameters().Any())
.ToArray();
//从BinaryReader读取每个字段的给定结构的操作
静态函数CreateReader()
{
//TODO:cache/validate T是一个“简单”结构
var br=Expression.Parameter(typeof(二进制读取器),“br”);
var info=typeof(T).GetTypeInfo();
var body=Expression.MemberInit(
表达式.New(typeof(T)),
从信息申报字段中的f开始
选择表达式。绑定(
F
表情,打电话(
比尔,
readers.Single(m=>m.ReturnType==f.FieldType),
Type.EmptyTypes,//不是泛型方法
新表达式[0]);
var function=Expression.Lambda(
身体,
新[]{br});
返回函数.Compile();
}
二进制序列化是否不存在?另一个问题是性能是否重要,涉及到AllocHGlobal
似乎有点奇怪。在每次调用中使用AllocHGlobal的好处是。我的实际实现稍微复杂一些,以提高效率。它缓存len、arr和ptr,以便每个实际调用都转换为字节仅涉及Marshal.StructureToPtr和Marshal.Copy。发布的代码只是一个简化的示例。二进制序列化的问题是开销。具有单个int32字段的结构将被序列化为大约140字节。如果您的结构相当大,则此开销不是什么大问题,但在我的场景中,我有一个一组相对较小的对象。因此,在我的情况下,将结构转换为它包含的实际4个字节是一个很大的节省。公平地说,MSDN在帮助确定可以使用哪些表达式方面并不友好。我确信反射/表达式组合可以在完整的.Net中为您构建一个转换器,但在Metro中则不太确定。我喜欢ap构建表达式并对其进行编译以获得最佳性能的方法。我认为Windows应用商店应用程序不允许这样做,因为它会动态生成要执行的代码,对吗?
static MethodInfo[] readers = typeof(BinaryReader).GetTypeInfo()
.DeclaredMethods
.Where(m => m.Name.StartsWith("Read") && !m.GetParameters().Any())
.ToArray();
// Action for a given struct that reads each field from a BinaryReader
static Func<BinaryReader, T> CreateReader<T>()
{
// TODO: cache/validate T is a "simple" struct
var br = Expression.Parameter(typeof(BinaryReader), "br");
var info = typeof(T).GetTypeInfo();
var body = Expression.MemberInit(
Expression.New(typeof(T)),
from f in info.DeclaredFields
select Expression.Bind(
f,
Expression.Call(
br,
readers.Single(m => m.ReturnType == f.FieldType),
Type.EmptyTypes, // Not a generic method
new Expression[0]));
var function = Expression.Lambda<Func<BinaryReader, T>>(
body,
new[] { br });
return function.Compile();
}