C # Reflection: Как инициализировать поле, созданное динамически в TypeBuilder? - PullRequest
1 голос
/ 14 октября 2011

Я создаю динамическую DLL для хранения пользовательских объектов, созданных из моей базы данных. Я могу создать поле так, как я хочу, однако я не понимаю, как вызвать конструктор. Для окончательного сгенерированного результата я хочу:

public class Countries
{
    public Countries() { }
    public static readonly ReferenceObject USA = new ReferenceObject(120);
    public static readonly ReferenceObject CAN = new ReferenceObject(13);
    public static readonly ReferenceObject MEX = new ReferenceObject(65);
    ... //These would be populated from the database
}

что я получаю

public class Countries
{
    public Countries() { }
    public static readonly ReferenceObject USA;
    public static readonly ReferenceObject CAN;
    public static readonly ReferenceObject MEX;
    ...
}

Как установить значения для новых инициализированных объектов?

AppDomain domain = AppDomain.CurrentDomain;

AssemblyName aName = new AssemblyName("DynamicEnums");
AssemblyBuilder ab = domain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Save);

ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");

foreach(ReferenceType rt in GetTypes())
{
    TypeBuilder tb = mb.DefineType(rt.Name, TypeAttributes.Public);

    foreach (Reference r in GetReferences(rt.ID))
    {
        string name = NameFix(r.Name);

        FieldBuilder fb = tb.DefineField(name, typeof(ReferenceObject), FieldAttributes.Static | FieldAttributes.Public | FieldAttributes.Literal);

        //Call constructor here... how???
    }

    types.Add(tb.CreateType());
}

ab.Save(aName.Name + ".dll");

Ответы [ 2 ]

4 голосов
/ 14 октября 2011

Немного копийной пасты из моего ответа на другой (очень похожий) вопрос, но:

AppDomain domain = AppDomain.CurrentDomain;

AssemblyName aName = new AssemblyName("DynamicEnums");
AssemblyBuilder ab = domain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Save);

ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");

// Store a handle to the ReferenceObject(int32 pValue)
// constructor.
ConstructorInfo referenceObjectConstructor = typeof(ReferenceObject).GetConstructor(new[] { typeof(int) });

foreach (ReferenceType rt in GetTypes())
{
    TypeBuilder tb = mb.DefineType(rt.Name, TypeAttributes.Public);

    // Define a static constructor to populate the ReferenceObject
    // fields.
    ConstructorBuilder staticConstructorBuilder = tb.DefineConstructor(MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, Type.EmptyTypes);
    ILGenerator staticConstructorILGenerator = staticConstructorBuilder.GetILGenerator();

    foreach (Reference r in GetReferences(rt.ID))
    {
        string name = r.Abbreviation.Trim();

        // Create a public, static, readonly field to store the
        // named ReferenceObject.
        FieldBuilder referenceObjectField = tb.DefineField(name, typeof(ReferenceObject), FieldAttributes.Static | FieldAttributes.Public | FieldAttributes.InitOnly);

        // Add code to the static constructor to populate the
        // ReferenceObject field:

        // Load the ReferenceObject's ID value onto the stack as a
        // literal 4-byte integer (Int32).
        staticConstructorILGenerator.Emit(OpCodes.Ldc_I4, r.ID);

        // Create a reference to a new ReferenceObject on the stack
        // by calling the ReferenceObject(int32 pValue) reference
        // we created earlier.
        staticConstructorILGenerator.Emit(OpCodes.Newobj, referenceObjectConstructor);

        // Store the ReferenceObject reference to the static
        // ReferenceObject field.
        staticConstructorILGenerator.Emit(OpCodes.Stsfld, referenceObjectField);
    }

    // Finish the static constructor.
    staticConstructorILGenerator.Emit(OpCodes.Ret);

    tb.CreateType();
}

ab.Save(aName.Name + ".dll");
1 голос
/ 14 октября 2011

Когда вы пишете класс C #, где поля инициализируются, как в вашем примере, компилятор генерирует код инициализации в конструкторе.

Это то, что я думаю, вы должны сделать: используйте TypeBuilder.DefineConstructor длясоздайте конструктор и, внутри, создайте код для установки полей.

...