Хорошо, я предположил, что Reference & ReferenceType выглядит примерно так:
public class ReferenceType
{
public string Name { get; set; }
public int ID { get; set; }
}
public class Reference
{
public string Abbreviation { get; set; }
public int ID { get; set; }
}
и класс, который вы пытаетесь сгенерировать, будет выглядеть примерно так:
public static class Countries
{
public static readonly ReferenceObject USA = new ReferenceObject(120);
public static readonly ReferenceObject CAN = new ReferenceObject(13);
//...
}
Что вам нужно сделать, так это создать набор полей (я сделал их статическими и доступными только для чтения, что является хорошей практикой, если вы пытаетесь имитировать перечисления), а затем заполнить их из статического конструктора, например:
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");
---- Редактировать ----
Чтобы получить доступ к значениям полей в сгенерированной DLL, у вас есть несколько вариантов. Первый - запустить этот код, взять копию файла «Dynamic Enums.dll», который он генерирует, и сослаться на него непосредственно из любого другого проекта, содержащего ваш код времени выполнения; у вас есть проект, который выполняется во время сборки для создания DLL (как указано выше) и второй, отдельный проект, который ссылается на DLL и выполняет работу вашего приложения во время выполнения. Преимущество этого состоит в том, что вы можете ссылаться на сгенерированные классы непосредственно в коде (например, SomeMethod(Countries.USA)
или if(someVariable == Countries.CAN)
), тогда как недостатком является то, что вы должны либо добавить приведенный выше код в процесс сборки, либо не забывать обновлять свои библиотеки DLL всякий раз, когда исходная база данных изменяется. Если это то, что вам нужно, я бы порекомендовал воспользоваться специальными инструментами для генерации кода, такими как T4, который встроен в Visual Studio.
Опция, которую вы, похоже, ищите выше, - это прямой доступ к динамической сборке, сгенерированной вами, пока она все еще находится в памяти. Для этого необходимо пометить сборку как работоспособную, так и сохраняемую:
AssemblyBuilder ab = domain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.RunAndSave);
По правде говоря, вы можете просто пометить его как AssemblyBuilderAccess.Run
, но я предполагаю, что вы все еще хотите сохранить вывод.
Затем вы можете использовать метод FieldInfo.GetValue (object obj) для получения статического значения:
foreach (Type t in types)
{
foreach (FieldInfo o in t.GetFields())
{
// As this is a static field no instance of type 't' is
// required to get the field value, so just pass null
ReferenceObject value = o.GetValue(null) as ReferenceObject;
Console.WriteLine("{0}.{1} = {2}", t, o.Name, value);
}
Console.WriteLine();
}