IL Emit TypeBuilder和解析引用

时间:2011-07-26 14:44:12

标签: c# reference reflection.emit il typebuilder

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

  1. 以某种方式按类依赖性对类进行排序,并按照依赖项的顺序“构建”它们,因此更依赖的类具有有效的构造函数引用来抓取。
  2. 在第一遍中分别定义所有构造函数(实际上没有为方法发出IL),以便定义所有构造函数引用。
  3. 以某种方式缓存已定义的构造函数,以便在构造函数尚未定义的情况下,我可以创建一个占位符ConstructorBuilder来获取引用,然后在最终发出构造函数时将其获取。
  4. 我正在尝试选项(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实例),我仍然很好奇。

2 个答案:

答案 0 :(得分:0)

我不是TypeBuilder的专家,但它有一个方法.GetConstructorhttp://msdn.microsoft.com/en-us/library/cs01xzbk.aspx)和.GetMethodhttp://msdn.microsoft.com/en-us/library/4s2kzbw8.aspx)应该可以返回如果在缓存的fieldType上调用它们,则声明构造函数...

答案 1 :(得分:0)

通过查看TypeBuilder的反汇编代码,看起来你不希望每种类型需要多个构造函数:

TypeBuilder.DefineConstructor只调用DefineConstructorNoLock,它只检查参数并增加constructorCount字段:

[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];
        }
    }