出于性能原因,我试图编写一个IL委托,该委托可以从字典中返回随机KeyValuePair
,而不必使用枚举。为此,我直接从字典的_bucket
和_entries
字段中读取。
Dictionary
是一个通用集合,但是我想避免不得不为每种单独的类型编译一个委托,而只返回一个装箱的对象。但是,以这种方式使用泛型似乎有些麻烦。
这是我目前所在的位置。请注意,实施尚未完成:
public static class DictionaryExtensions
{
private delegate object RandomDelegate( IDictionary dict, Random random );
private static RandomDelegate randomDel;
static DictionaryExtensions()
{
randomDel = CompileRandomDel();
}
public static object RandomValue<TKey, TValue>( this Dictionary<TKey, TValue> dict, Random rand )
{
var x = randomDel( dict, rand );
return x;
}
private static RandomDelegate CompileRandomDel()
{
var bucketsField = typeof( Dictionary<,> ).GetField( "_buckets", BindingFlags.Instance | BindingFlags.NonPublic );
var entriesField = typeof( Dictionary<,> ).GetField( "_entries", BindingFlags.Instance | BindingFlags.NonPublic );
var randNext = typeof( Random ).GetMethod( "Next", Type.EmptyTypes );
var method = new DynamicMethod(
"RandomEntry",
MethodAttributes.Public | MethodAttributes.Static,
CallingConventions.Standard,
typeof( object ),
new[] { typeof( Dictionary<,> ), typeof( Random ) },
typeof( DictionaryExtensions ),
false );
var il = method.GetILGenerator();
il.DeclareLocal( typeof( int ) ); // Loc_0: Bucket
il.Emit( OpCodes.Ldarg_1 ); // Load random
il.Emit( OpCodes.Call, randNext ); // Get next random int
//il.Emit( OpCodes.Ldarg_0 ); // Load dictionary
//il.Emit( OpCodes.Ldfld, bucketsField ); // Load buckets
//il.Emit( OpCodes.Ldlen ); // Load buckets length
//il.Emit( OpCodes.Rem ); // random % bucket count
//il.Emit( OpCodes.Ldelem ); // Load bucket
//il.Emit( OpCodes.Stloc_0 ); // Store bucket in loc_0
//il.Emit( OpCodes.Ldarg_0 ); // Load dictionary
//il.Emit( OpCodes.Ldfld, entriesField ); // Load dictionary entries
//il.Emit( OpCodes.Ldloc_0 ); // Load bucket
//il.Emit( OpCodes.Ldelem ); // Load element at bucket
// Debug (just returning the random int for now)
il.Emit( OpCodes.Conv_I4 );
il.Emit( OpCodes.Box, typeof( int ) );
il.Emit( OpCodes.Ret );
return ( RandomDelegate ) method.CreateDelegate( typeof( RandomDelegate ) );
}
}
我的问题似乎是缺少特定的通用参数。运行该方法将导致TypeInitialization: An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B).
如果我将DynamicMethod的参数从Dictionary<,>
更改为IDictionary
,则可以使用。但是,这很危险,因为并非所有IDictionary
实现都相等,并且该委托只能用于实际的Dictionary<,>
类型。除此之外,看来IDictionary
的使用可能会加重字段的加载,因为当我尝试仅加载InvalidProgramException
的长度时,它会抛出_entries
字段并返回。
因此,使用通用类型而不指定参数是不行的。 还有其他方法可以编译适用于所有泛型类型参数的安全委托吗?由于返回值只是一个装箱的object
,我认为不需要编译委托对于每种类型,它都会遇到,但是如果不可避免,我想我将别无选择。