我正在通过发出IL编写面向性能的数据反序列化器。序列化的数据为UTF8
,并且字段表示为字符串。
[FieldA]: 22
[FieldB]: 16
我已经编写了一个自定义阅读器,该阅读器可以正确地对序列化的数据进行标记,并在逐步浏览序列化的数据时提供ReadOnlySpan<byte>
。
我希望有一个静态的,内联的反序列化器,它能够打包字段的字节签名,以便我可以轻松地创建一个跳转表并设置适当的字段。
非动态代码的外观:
// byteSpan is a ReadOnlySpan<byte> containing the signature
var signatureA = Encoding.UTF8.GetBytes( "FieldA" );
var signatureB = Encoding.UTF8.GetBytes( "FieldB" );
if( byteSpan.SequenceEqual( signatureA ) )
DoSomething();
else if ( byteSpan.SequenceEqual( signatureB ) )
DoSomething();
...
如何生成跳转表:
var fieldSignatures = GetTypeSignatures<T>(); // Returns a Tuple<byte[], FieldInfo>
var setFieldLabels = new List<Tuple<FieldInfo, Label>>();
foreach( (byte[] signature, FieldInfo field) in fieldSignatures )
{
var setFieldLabel = il.DefineLabel();
setFieldLabels.Add( Tuple.Create( field, setFieldLabel ) );
il.Emit( OpCodes.Ldloc_1 ); // Load the current ReadOnlySpan<byte>
// Inline load byte[] signature here
il.Emit( OpCodes.Call, METHOD_SEQUENCEEQUAL );
il.Emit( OpCodes.Brtrue, setFieldLabel );
}
EmitFieldSetters( setFieldLabels, ref il );
是否可以将签名字节数组直接烘焙到要发出的IL中,使它们成为委托的一部分?
这些签名是在运行时根据类型信息生成的,因此在静态类中手动定义它们是不可行的。一种解决方法是定义一个新的动态Assembly
和Type
并将字节存储在其中,但我想避免这样做,如果可能的话。
答案 0 :(得分:2)
您可能想做的是将签名字节数组(byte[][]
)的数组作为动态方法的隐藏第一个参数传递。
您可以通过以下方式加载适当的字节数组:
// Load the first byte[][] signatures array argument
il.Emit( OpCodes.LdArg_0 );
// Load the index into the signatures array
il.Emit( OpCodes.Ldc_I4, signatureIndex );
// Fetch the signature byte[] element from the array
il.Emit( OpCodes.Ldelem_Ref );
然后,当您通过动态方法创建委托时,可以使用带有目标对象的重载,该对象成为(隐藏的)第一个参数:
var deserializerDelegate = dynamicMethod.CreateDelegate(typeof(YourDelegateType), signatures);
总而言之,请看我对上述问题的评论,该问题涉及使用替代签名查找算法,即使使用动态生成的IL,该算法也可能比线性搜索最佳。