C# IL Emit TypeBuilder和解析引用

C# IL Emit TypeBuilder和解析引用,c#,reference,reflection.emit,il,typebuilder,C#,Reference,Reflection.emit,Il,Typebuilder,我发布了几个类,其中一些需要在自己的构造函数中构造它们的对等体。没有无限的递归依赖关系(因此,如果A构造B,B就不会构造A;这对于嵌套引用也是如此[A构造B构造C意味着B和C都不会构造A])。我目前正在编写发出构造函数的代码,我有点问题。我不知道依赖关系的顺序,所以我似乎有几个选择: 以某种方式按依赖项对类进行排序,并按照依赖项的顺序“构建”它们,因此依赖性越强的类就有一个有效的构造函数引用可供获取 在第一个过程中分别定义所有构造函数(不实际发出方法的IL),以便定义所有构造函数引用 以某种方式

我发布了几个类,其中一些需要在自己的构造函数中构造它们的对等体。没有无限的递归依赖关系(因此,如果A构造B,B就不会构造A;这对于嵌套引用也是如此[A构造B构造C意味着B和C都不会构造A])。我目前正在编写发出构造函数的代码,我有点问题。我不知道依赖关系的顺序,所以我似乎有几个选择:

  • 以某种方式按依赖项对类进行排序,并按照依赖项的顺序“构建”它们,因此依赖性越强的类就有一个有效的构造函数引用可供获取
  • 在第一个过程中分别定义所有构造函数(不实际发出方法的IL),以便定义所有构造函数引用
  • 以某种方式缓存已定义的构造函数,这样,如果尚未定义构造函数,我可以创建一个占位符ConstructorBuilder来获取引用,当构造函数最终发出时,引用将被抓取
  • 我目前正在尝试选项(3),我想知道是否已经有一种方法可以从TypeBuilder实现这一点。我有这样的代码(在需要时获取构造函数引用):

    我的构建方法目前是这样开始的(我认为如果之前定义了构造函数,这将不起作用):

    是否有某种方法可以在不创建自己的显式缓存的情况下查找以前定义的ConstructorBuilder(如果已定义)?似乎TypeBuilder应该知道这一点,但我看不到任何明显的方式从TypeBuilder文档中查找它


    编辑:


    我最终选择了路径(2),它在第一个过程中定义了所有相关的方法,然后在第二个过程中发出IL。我仍然很好奇是否可以从TypeBuilder中为其他地方已经定义的构建器获取MethodBuilder实例(或ConstructorBuilder实例)。

    我对
    TypeBuilder
    不是专家,但它有一个方法
    .GetConstructor
    ()和
    .GetMethod
    ()如果在缓存的fieldType上调用它们,则应该能够返回声明的构造函数

    通过查看TypeBuilder的反汇编代码,您不可能希望每个类型有多个构造函数:

    TypeBuilder.DefineConstructor
    只调用
    DefineConstructorNoLock
    ,它只检查参数并递增构造函数计数字段:

    [SecurityCritical]
    private ConstructorBuilder DefineConstructorNoLock(MethodAttributes attributes, CallingConventions callingConvention, Type[] parameterTypes, Type[][] requiredCustomModifiers, Type[][] optionalCustomModifiers)
    {
        this.CheckContext(parameterTypes);
        this.CheckContext(requiredCustomModifiers);
        this.CheckContext(optionalCustomModifiers);
        this.ThrowIfCreated();
        string name;
        if ((attributes & MethodAttributes.Static) == MethodAttributes.PrivateScope)
        {
            name = ConstructorInfo.ConstructorName;
        }
        else
        {
            name = ConstructorInfo.TypeConstructorName;
        }
        attributes |= MethodAttributes.SpecialName;
        ConstructorBuilder result = new ConstructorBuilder(name, attributes, callingConvention, parameterTypes, requiredCustomModifiers, optionalCustomModifiers, this.m_module, this);
        this.m_constructorCount++;
        return result;
    }
    
    因此,如果您只想为每个类型定义一个构造函数,可以检查此属性(使用反射,因为它是一个私有字段)并检查其值:

    namespace ConsoleApplication7
    {
        static class TypeBuilderExtension
        {
            public static int GetConstructorCount(this TypeBuilder t)
            {
                FieldInfo constCountField = typeof(TypeBuilder).GetField("m_constructorCount", BindingFlags.NonPublic | BindingFlags.Instance);
                return (int) constCountField.GetValue(t);
            }
        }
    
        class Program
        {  
            static void Main(string[] args)
            {
                AppDomain ad = AppDomain.CurrentDomain;
                AssemblyBuilder ab = ad.DefineDynamicAssembly(new AssemblyName("toto.dll"), AssemblyBuilderAccess.RunAndSave);
                ModuleBuilder mb = ab.DefineDynamicModule("toto.dll");
                TypeBuilder tb = mb.DefineType("mytype");
                
                Console.WriteLine("before constructor creation : " + tb.GetConstructorCount());
    
                ConstructorBuilder cb = tb.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new Type[0]);
                ILGenerator ilgen = cb.GetILGenerator();
                ilgen.Emit(OpCodes.Ret);
                Console.WriteLine("after constructor creation : " + tb.GetConstructorCount());
    
                tb.CreateType();
                ab.Save("toto.dll");
            }
        }
    }
    
    哪些产出:

    创建构造函数之前:0

    构造函数创建后:1

    这不会给出实际的构造函数生成器,但您会知道您已经定义了它

    如果您想实际获取constructorBuilder,并且不想创建太多重载(例如1),我会选择带有扩展方法的选项3:

        static class TypeBuilderExtension
        {
            private static Dictionary<TypeBuilder, ConstructorBuilder> _cache = new Dictionary<TypeBuilder, ConstructorBuilder>();
    
            public static ConstructorBuilder DefineMyConstructor(this TypeBuilder tb)
            {
                if (!_cache.ContainsKey(tb))
                {
                    _cache.Add(tb, tb.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new Type[0]));
                }
    
                return _cache[tb];
            }
        }
    
    静态类TypeBuilderExtension
    {
    私有静态字典_cache=新字典();
    公共静态构造函数builder DefineMyConstructor(此类型生成器tb)
    {
    如果(!\u缓存.容器大小(tb))
    {
    _Add(tb,tb.DefineConstructor(MethodAttributes.Public,CallingConventions.HasThis,新类型[0]);
    }
    返回_缓存[tb];
    }
    }
    
    GetConstructor返回一个ConstructorInfo,它不允许我访问ILGenerator。我相信我可以使用GetConstructor找到调用构造函数的引用,但这意味着我需要先定义它。
    namespace ConsoleApplication7
    {
        static class TypeBuilderExtension
        {
            public static int GetConstructorCount(this TypeBuilder t)
            {
                FieldInfo constCountField = typeof(TypeBuilder).GetField("m_constructorCount", BindingFlags.NonPublic | BindingFlags.Instance);
                return (int) constCountField.GetValue(t);
            }
        }
    
        class Program
        {  
            static void Main(string[] args)
            {
                AppDomain ad = AppDomain.CurrentDomain;
                AssemblyBuilder ab = ad.DefineDynamicAssembly(new AssemblyName("toto.dll"), AssemblyBuilderAccess.RunAndSave);
                ModuleBuilder mb = ab.DefineDynamicModule("toto.dll");
                TypeBuilder tb = mb.DefineType("mytype");
                
                Console.WriteLine("before constructor creation : " + tb.GetConstructorCount());
    
                ConstructorBuilder cb = tb.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new Type[0]);
                ILGenerator ilgen = cb.GetILGenerator();
                ilgen.Emit(OpCodes.Ret);
                Console.WriteLine("after constructor creation : " + tb.GetConstructorCount());
    
                tb.CreateType();
                ab.Save("toto.dll");
            }
        }
    }
    
        static class TypeBuilderExtension
        {
            private static Dictionary<TypeBuilder, ConstructorBuilder> _cache = new Dictionary<TypeBuilder, ConstructorBuilder>();
    
            public static ConstructorBuilder DefineMyConstructor(this TypeBuilder tb)
            {
                if (!_cache.ContainsKey(tb))
                {
                    _cache.Add(tb, tb.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new Type[0]));
                }
    
                return _cache[tb];
            }
        }