【发布时间】:2011-12-07 15:22:35
【问题描述】:
微软在这里展示了如何创建动态类:
http://msdn.microsoft.com/en-us/library/system.reflection.emit.modulebuilder(v=vs.71).aspx
这定义了一个自定义对象,它们在其中定义了一个构造函数和一个方法。我定义了一个类,有没有办法发出我已经编写的类,而不是像示例所示那样尝试编写它?
感谢 FacticiusVir,它几乎完成了。然而,它似乎并不完全存在,'Countries.USA 不受该语言的支持'
包含 FacticiusVir 答案的完整代码:
class DynamicEnums
{
public static void Main()
{
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");
ConstructorInfo referenceObjectConstructor = typeof(ReferenceObject).GetConstructor(new[] { typeof(int) });
List<Type> types = new List<Type>();
foreach(ReferenceType rt in GetTypes())
{
TypeBuilder tb = mb.DefineType(rt.Name, TypeAttributes.Public);
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;
if (rt.Name == "Countries")
name = r.Abbreviation.Trim();
else if (rt.Name == "PermanentFundDividends")
name = "Year" + r.Abbreviation.Trim();
else
name = NameFix(r.Name);
// 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);
}
staticConstructorILGenerator.Emit(OpCodes.Ret);
types.Add(tb.CreateType());
}
try
{
ab.Save(aName.Name + ".dll");
}
catch (Exception)
{
Console.WriteLine("Could not save .dll, file must already be loaded.");
}
foreach (Type t in types)
{
foreach (FieldInfo o in t.GetFields())
{
Console.WriteLine("{0}.{1} = {2}", t, o.Name, "Later"); //Don't know how to get Value doing it this way
}
Console.WriteLine();
//Console.ReadKey();
}
Console.WriteLine();
Console.WriteLine("Dynamic Enums Built Successfully.");
//Console.ReadKey();
}
public static List<ReferenceType> GetTypes()
{
List<ReferenceType> referenceTypes = new List<ReferenceType>();
referenceTypes.Add(new ReferenceType { ID = 1, Name = "Countries" });
return referenceTypes;
}
public static List<Reference> GetReferences(int typeID)
{
List<Reference> references = new List<Reference>();
references.Add(new Reference { ID = 120, Abbreviation = "USA" });
return references;
}
public struct ReferenceType
{
public int ID;
public string Name;
}
public struct Reference
{
public int ID;
public int TypeID;
public string Abbreviation;
public string Name;
}
public static string NameFix(string name)
{
//Strip all non alphanumeric characters
string r = Regex.Replace(name, @"[^\w]", "");
//Enums cannot begin with a number
if (Regex.IsMatch(r, @"^\d"))
r = "N" + r;
return r;
}
}
public class ReferenceObject
{
private readonly int value;
public ReferenceObject(int pValue)
{
value = pValue;
}
public override string ToString()
{
return value.ToString();
}
public int Value()
{
return value;
}
public int ID()
{
return value;
}
#region == Operator
public static bool operator ==(int objLeft, ReferenceObject objRight)
{
return objLeft == objRight.value;
}
public static bool operator ==(ReferenceObject objLeft, int objRight)
{
return objLeft.value == objRight;
}
public static bool operator ==(string objLeft, ReferenceObject objRight)
{
return objLeft == objRight.value.ToString();
}
public static bool operator ==(ReferenceObject objLeft, string objRight)
{
return objLeft.value.ToString() == objRight;
}
#endregion
#region != Operator
public static bool operator !=(int objLeft, ReferenceObject objRight)
{
return objLeft != objRight.value;
}
public static bool operator !=(ReferenceObject objLeft, int objRight)
{
return objLeft.value != objRight;
}
public static bool operator !=(string objLeft, ReferenceObject objRight)
{
return objLeft != objRight.value.ToString();
}
public static bool operator !=(ReferenceObject objLeft, string objRight)
{
return objLeft.value.ToString() != objRight;
}
#endregion
public override bool Equals(object obj)
{
if ((obj is ReferenceObject))
return value == ((ReferenceObject)obj).value;
if ((obj is int))
return value == (int)obj;
if ((obj is string))
return value.ToString() == (string)obj;
return false;
}
public override int GetHashCode()
{
return value;
}
}
【问题讨论】:
-
您的
Countries对象,如所写,无法编译。您是否想要一个包含一组命名字段的 Country 类,每个字段都是 ReferencedObject?如果您可以在 C# 中给出您想要的可编译示例,那么为它编写 Reflection.Emit 代码会更容易。 -
另外,您有 ReferenceType 和 Reference 的定义吗?
-
您给出的示例不会产生您所描述的错误 - 具体是什么给您“Countries.USA is not supported by the language”?
-
我添加了对已编译 dll 的引用,然后尝试使用 120 == countries.USA。我可能已经对其进行了排序(有点),我使用 ReferenceObject 类定义创建了一个 C# .dll 项目,将其复制到我的 Assembly 目录,然后在 DynamicEnum 项目(构建动态 dll 的项目)中引用了该 .dll。
-
我一直在寻找一种方法将代码中的类添加到动态 dll 中,但无济于事......但也许我正在尝试做一些意想不到的事情。
标签: c# .net reflection dll dynamic