没有类型参数的C#通用DynamicMethod

时间:2019-03-06 16:05:23

标签: c# delegates cil

出于性能原因,我试图编写一个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,我认为不需要编译委托对于每种类型,它都会遇到,但是如果不可避免,我想我将别无选择。

0 个答案:

没有答案
相关问题