Topic: c# - 动态的创建新 property 对象
标题: c# - Create new property object on the fly
本文是关于如何从string和其他的信息创建出类型和属性(Types and the Properties)。采取的方法是System.Emit.
this page is about creating properties from strings
and other information and you use the System.Emit (which still is the
Reflection system) to build the Types and the Properties that we are interested
in.
假设我们要解决的问题是,我们有一个DynamicClass,它有如下的属性。
The problem is
suppose that we have a DynamicClass, and it has the
following information.
-
public class DynamicClass
-
{
-
public Type ObjectType { get; set; }
-
public List<string> PropertyNameList { get; set; }
-
public List<Type> PropertyTypeList { get; set; }
-
}
从该类,我们想生成PropertiesInfo,生成这些PropertiesInfo的信息在PropertyNameList和其他的属性里面).
from this class, we 'd like get PropertiesInfo's
(the properties is represented in the above code with PropertyNameList and
others.)
-
public PropertyInfo[] GetProperties (object o)
-
{
-
PropertyInfo[] properties = o.GetType().GetProperties();
-
-
return properties;
-
}
我们首先需要的是创建一个表示该类型的信息对象。
So we can first create a class to represent the information
about a property.
-
public class Field
-
{
-
public string FieldName;
-
public Type FieldType;
-
}
-
public class Field
-
{
-
public string FieldName;
-
public Type FieldType;
-
}
我们生成了一个MyObjectBuilder类,使用Emit空间动态的创建模块,在该创建的模块中定义了属性和默认的构造器。
And we have the MyObjectBuilder class, which is to use the
Emit namespace to dynamically create the modules and then use that module to
define properties and default constructors.
-
public class MyObjectBuilder
-
{
-
-
public Type objType { get; set; }
-
-
public MyObjectBuilder()
-
{
-
this.objType = null;
-
}
-
-
public object CreateNewObject(List<Field> Fields)
-
{
-
this.objType = CompileResultType(Fields);
-
var myObj = Activator.CreateInstance(this.objType);
-
return myObj;
-
}
-
-
// Given MyObjectBuilder, build a List item.
-
public IList getObjectLsit()
-
{
-
Type listType = typeof(List<>).MakeGenericType(this.objType);
-
return (IList)Activator.CreateInstance(listType);
-
}
-
-
-
// Why do we need the CompareTo method?
-
public static MethodInfo GetCompareToMethod(object genericInstance, string sortExpression)
-
{
-
Type genericType = genericInstance.GetType();
-
object sortExpressionValue = genericType.GetProperty(sortExpression).GetValue(genericInstance, null);
-
Type sortExpressionType = sortExpression.GetType();
-
MethodInfo compareToMethodOfSortExpressionType = sortExpressionType.GetMethod("CompareTo", new Type[] { sortExpressionType });
-
-
return compareToMethodOfSortExpressionType;
-
}
-
-
// Compile the Info passed in and get a result back
-
public static Type CompileResultType(List<Field> Fields)
-
{
-
TypeBuilder tb = GetTypeBuilder();
-
// A default constructor is the void constructor
-
ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
-
-
foreach (var field in Fields)
-
{
-
CreateProperty(tb, field.FieldName, field.FieldType);
-
}
-
Type objectType = tb.CreateType();
-
return objectType;
-
}
-
-
// Create a TypeBuilder, which should we can further to build Constructor, Properties and etc..
-
private static TypeBuilder GetTypeBuilder()
-
{
-
var typeSignature = "MyDynamicType";
-
var an = new AssemblyName(typeSignature);
-
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
-
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
-
TypeBuilder tb = moduleBuilder.DefineType(typeSignature,
-
TypeAttributes.Public
-
| TypeAttributes.Class
-
| TypeAttributes.AutoClass
-
| TypeAttributes.AnsiClass
-
| TypeAttributes.BeforeFieldInit
-
| TypeAttributes.AutoLayout,
-
null);
-
return tb;
-
}
-
-
// Try to create a Property with all the necessary information passed in, such as the TypeBuilder.
-
//
-
private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
-
{
-
FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
-
-
PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName
-
, PropertyAttributes.HasDefault
-
, propertyType
-
, null);
-
MethodBuilder getPropMethodBldr = tb.DefineMethod("get_" + propertyName
-
, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig
-
, propertyType
-
, Type.EmptyTypes);
-
-
ILGenerator getIl = getPropMethodBldr.GetILGenerator();
-
getIl.Emit(OpCodes.Ldarg_0);
-
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
-
getIl.Emit(OpCodes.Ret);
-
-
MethodBuilder setPropMthBldr = tb.DefineMethod("set_" + propertyName,
-
MethodAttributes.Public |
-
MethodAttributes.SpecialName |
-
MethodAttributes.HideBySig,
-
null,
-
new[] { propertyType }
-
);
-
-
ILGenerator setIl = setPropMthBldr.GetILGenerator();
-
Label modifyProperty = setIl.DefineLabel();
-
Label exitSet = setIl.DefineLabel();
-
-
setIl.MarkLabel(modifyProperty);
-
setIl.Emit(OpCodes.Ldarg_0);
-
setIl.Emit(OpCodes.Ldarg_1);
-
setIl.Emit(OpCodes.Stfld, fieldBuilder); // why it is the Builder classes, rather than the actual field info?
-
-
setIl.Emit(OpCodes.Nop);
-
-
setIl.MarkLabel(exitSet);
-
setIl.Emit(OpCodes.Ret);
-
-
propertyBuilder.SetGetMethod(getPropMethodBldr);
-
propertyBuilder.SetSetMethod(setPropMthBldr);
-
}
-
-
}
写了如下的驱动代码。
And then we can drive the code with the following driving
code.
-
public class MyPropertyBuilder
-
{
-
public static void SetMemberValue(MemberInfo member, object target, object value)
-
{
-
switch (member.MemberType)
-
{
-
case MemberTypes.Field:
-
((FieldInfo)member).SetValue(target, value);
-
break;
-
((PropertyInfo) member).SetValue(target, value, null);
-
case MemberTypes.Property:
-
break;
-
default:
-
throw new ArgumentException("MemberInfo must be type FieldInfo or PropertyInfo");
-
}
-
}
-
-
public void BuildProperties()
-
{
-
// Creating a Lits of Fields (string fieldName, Type fieldType);
-
List<Field> Fields = new List<Field>();
-
Fields.Add(new Field() { FieldName = "TestName", FieldType = typeof(string)});
-
// MyObjectBuilder class
-
MyObjectBuilder o = new MyObjectBuilder();
-
-
// creating a new object dynamically
-
object newObj = o.CreateNewObject(Fields);
-
IList objList = o.getObjectLsit();
-
-
Type t = newObj.GetType();
-
object instance = Activator.CreateInstance(t);
-
-
PropertyInfo[] props = instance.GetType().GetProperties();
-
int instancePropsCount = props.Count();
-
-
for (int i = 0; i < instancePropsCount; i++)
-
{
-
string fieldName = props[i].Name;
-
MemberInfo[] mInfo = null;
-
PropertyInfo pInfo = newObj.GetType().GetProperty(fieldName);
-
-
if (pInfo != null)
-
{
-
var value = pInfo.GetValue(newObj, null);
-
mInfo = t.GetMember(fieldName);
-
-
if (value != null && mInfo != null && !string.IsNullOrEmpty(mInfo[0].ToString()))
-
{
-
SetMemberValue(mInfo[0], instance, value);
-
}
-
}
-
else
-
{
-
mInfo = t.GetMember(fieldName);
-
if (mInfo != null && !string.IsNullOrEmpty(mInfo[0].ToString()))
-
{
-
SetMemberValue(mInfo[0], instance, null);
-
}
-
}
-
objList.Add(instance);
-
}
-
}
-
-
}
主程序是
And the code that runs the program is
-
static void Main(string[] args)
-
{
-
MyPropertyBuilder propBuilder = new MyPropertyBuilder();
-
-
propBuilder.BuildProperties();
-
-
-
}
运行该程序,打开模块调试窗口,你会看到如下的信息,表示一个动态的模块被创建出来了。
And if you run it, you will see the Modules have
the dynamic module loaded.
At debug, when you check the Modules that is
loaded, you can check with the follownig.
-
MyDynamicType MyDynamicType No No No symbols loaded. 16 [10216] DemoPropertyBuilder.vshost.exe: Managed (v4.0.30319)
References
:
:
阅读(659) | 评论(0) | 转发(0) |