3

我正在发出几个类,其中一些需要在它们自己的构造函数中构造它们的对等点。没有无限的递归依赖(因此,如果 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 实例)。

4

2 回答 2

0

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

于 2011-07-26T14:57:08.137 回答
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,但您会知道您已经定义了它。

如果您想实际获得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];
        }
    }
于 2017-01-09T09:46:51.837 回答