不带类型参数的C#泛型DynamicMethod
出于性能原因,我正在尝试编写一个IL委托,它可以从字典中返回随机的不带类型参数的C#泛型DynamicMethod,c#,delegates,cil,C#,Delegates,Cil,出于性能原因,我正在尝试编写一个IL委托,它可以从字典中返回随机的KeyValuePair,而无需使用枚举。为此,我直接从字典的\u bucket和\u条目字段中读取 Dictionary是一个泛型集合,但我希望避免为每个单独的类型编译委托,而只返回一个装箱对象。然而,以这种方式使用泛型似乎有点麻烦 这就是我目前的处境。请注意,实施尚未完成: public static class DictionaryExtensions { private delegate object Rando
KeyValuePair
,而无需使用枚举。为此,我直接从字典的\u bucket
和\u条目
字段中读取
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 ) );
}
}
公共静态类字典扩展
{
私有委托对象随机委托(IDictionary dict,Random Random);
私有静态随机数据集;
静态字典扩展()
{
randomDel=CompileRandomDel();
}
公共静态对象随机值(此字典dict dict,Random rand)
{
var x=随机数据集(dict,rand);
返回x;
}
私有静态RandomDelegate编译器
{
var bucketsField=typeof(Dictionary.GetField(“_bucket”,BindingFlags.Instance | BindingFlags.NonPublic”);
var entriesField=typeof(Dictionary.GetField(“_entries”,BindingFlags.Instance | BindingFlags.NonPublic”);
var randNext=typeof(Random).GetMethod(“Next”,Type.EmptyTypes);
var方法=新的DynamicMethod(
“随机输入”,
MethodAttributes.Public | MethodAttributes.Static,
呼叫约定。标准,
类型(对象),
新[]{typeof(字典),typeof(随机)},
类型(字典扩展),
假);
var il=method.GetILGenerator();
il.DeclareLocal(typeof(int));//Loc_0:Bucket
il.Emit(opcode.Ldarg_1);//随机加载
il.Emit(OpCodes.Call,randNext);//获取下一个随机整数
//il.Emit(OpCodes.Ldarg_0);//加载字典
//il.Emit(操作码.Ldfld,bucketsField);//加载存储桶
//il.Emit(opcode.Ldlen);//加载桶长度
//il.Emit(OpCodes.Rem);//随机%bucket计数
//il.Emit(opcode.Ldelem);//加载桶
//il.Emit(opcode.Stloc_0);//将存储桶存储在loc_0中
//il.Emit(OpCodes.Ldarg_0);//加载字典
//il.Emit(opcode.Ldfld,entriesField);//加载字典条目
//il.Emit(opcode.Ldloc_0);//加载桶
//il.Emit(OpCodes.Ldelem);//铲斗处的加载元素
//调试(现在只返回随机整数)
il.Emit(操作码.Conv_I4);
发射(操作码框,类型(int));
发射(操作码Ret);
return(RandomDelegate)方法.CreateDelegate(typeof(RandomDelegate));
}
}
我的问题似乎是缺少具体的通用参数。运行该方法会导致TypeInitialization:试图加载格式不正确的程序。(HRESULT的例外:0x8007000B)。
如果我将DynamicMethod的参数从Dictionary
更改为IDictionary
,这会起作用。但是,这是危险的,因为并非所有的IDictionary
实现都是相同的,并且此委托仅用于实际的字典
类型。除此之外,IDictionary
的使用可能会破坏字段的加载,因为当我尝试加载\u条目的长度并返回它时,它抛出了一个invalidProgrammeException
因此,使用泛型类型而不指定参数是行不通的有没有其他方法可以编译一个可以处理所有泛型类型参数的安全委托?因为返回值只是一个装箱的对象
我认为没有必要为它遇到的每种类型编译委托,但如果不可避免,我想我别无选择。简言之:不,不完全,因为泛型类型可能发生代码专门化。请参见此处:(本质上,使用不同泛型类型参数的同一泛型类型不一定共享代码,这将导致无法使用单个委托仅指向一个方法项,因为对于不同的泛型类型参数,可能存在不同的代码基)如果泛型类型参数被约束为引用类型,那么代码共享就会发生……为什么您反对为使用它的每个字典类型创建一个新的动态方法?这是非常通用的,可以说是最容易实现的。另一个选项是使用泛型类型/方法创建动态程序集,唯一的性能差异是调用动态方法和动态程序集中的方法之间的差异。简言之:不,不完全是这样,因为泛型类型可能发生代码专门化。请参见此处:(本质上,使用不同泛型类型参数的同一泛型类型不一定共享代码,这将导致无法使用单个委托仅指向一个方法项,因为对于不同的泛型类型参数,可能存在不同的代码基)如果泛型类型参数被约束为引用类型,那么代码共享就会发生……为什么您反对为使用它的每个字典类型创建一个新的动态方法?这是非常通用的,可以说是最容易实现的。另一个选项是使用泛型类型/方法创建动态程序集,唯一的性能差异是调用动态方法和动态程序集中的方法之间的差异。