IL 发出类型生成器和解析引用

本文关键字:和解 引用 类型 IL | 更新日期: 2023-09-27 18:03:27

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

  1. 以某种方式按依赖关系对类进行排序,并按其依赖关系的顺序"构建"它们,以便更依赖的类具有有效的构造函数引用。
  2. 在第一次传递中单独定义所有构造函数(不实际发出方法的 IL(,以便定义所有构造函数引用。
  3. 以某种方式缓存定义的构造函数,以便如果尚未定义构造函数,我可以创建一个占位符 ConstructorBuilder 来获取引用,然后在最终发出构造函数时获取该引用。

我目前正在尝试选项 (3(,我想知道是否已经有一种方法可以从 TypeBuilder 中做到这一点。 我有如下所示的代码(在需要时获取构造函数引用(:

            var fieldType = DefineType(udtField.Type); // This looks up a cached TypeBuilder or creates a placeholder, if needed
            var constructor = fieldType.GetConstructor(Type.EmptyTypes);
            if (constructor == null)
            {
                constructor =
                    fieldType.DefineConstructor(
                        MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
                        CallingConventions.Standard, Type.EmptyTypes);
            }

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

    private void BuildConstructor()
    {
        var method =
            DefinedType.DefineConstructor(
                MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
                CallingConventions.Standard, Type.EmptyTypes);
        var il = method.GetILGenerator();

有没有办法可以查找以前定义的 ConstructorBuilder(如果有的话(,而不必创建自己的显式缓存? 似乎TypeBuilder应该知道它,但是我看不到任何明显的方法来从TypeBuilder文档中查找它。


编辑:

我最终沿着路线 (2( 走下去,它在第一次传递期间定义所有相关方法,然后在第二次传递中发出 IL。 我仍然很好奇是否有可能从TypeBuilder中获取MethodBuilder实例(或ConstructorBuilder实例(,用于已经在其他地方定义的构建器。

IL 发出类型生成器和解析引用

我不是TypeBuilder方面的专家,但它有一个方法.GetConstructor(http://msdn.microsoft.com/en-us/library/cs01xzbk.aspx(和.GetMethod(http://msdn.microsoft.com/en-us/library/4s2kzbw8.aspx(,如果你在缓存的字段类型上调用它们,它们应该能够返回声明的构造函数...

通过查看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,但你会知道你已经定义了它。

如果你想实际获得 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];
        }
    }