首页 > 解决方案 > 没有类型参数的 C# Generic DynamicMethod

问题描述

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

标签: c#delegatescil

解决方案


推荐阅读