しがないプログラマ の日記 さんで クラスの動的生成 ってエントリーがありますけど、
Horiuchi_Hさんが作ってるクラスでは、プロパティがString固定です。
これだで十分な場合も十二分に多いんですけど、ちと使いづらい時もあります。
なので、勝手にいじらせてもらいました。
【 動的クラス生成クラス 】
public class CreateType
{
// 設定されているプロパティを取得します
public interface IUserData
{
string[] GetProperties();
}
private static Type Create(string className, Dictionary<String, Type> Properties)
{
AppDomain domain = AppDomain.CurrentDomain;
AssemblyName assemblyName = new AssemblyName();
assemblyName.Name = "TempAssembly.dll";
AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name);
TypeBuilder typeBuilder = moduleBuilder.DefineType(className, TypeAttributes.Public | TypeAttributes.Class, typeof(object), new Type[] { typeof(IUserData) });
// GetPropertiesを実装
MethodBuilder getPropsMethod = typeBuilder.DefineMethod("GetProperties", MethodAttributes.Public | MethodAttributes.Virtual, typeof(string[]), Type.EmptyTypes);
ILGenerator getPropsIL = getPropsMethod.GetILGenerator();
getPropsIL.DeclareLocal(typeof(string[]));
LoadInteger(getPropsIL, Properties.Count);
getPropsIL.Emit(OpCodes.Newarr, typeof(string));
getPropsIL.Emit(OpCodes.Stloc_0);
for (int index = 0; index < Properties.Count; index++)
{
getPropsIL.Emit(OpCodes.Ldloc_0);
LoadInteger(getPropsIL, index);
getPropsIL.Emit(OpCodes.Ldstr, Properties.Keys.ElementAt( index ) );
getPropsIL.Emit(OpCodes.Stelem_Ref);
}
getPropsIL.Emit(OpCodes.Ldloc_0);
getPropsIL.Emit(OpCodes.Ret);
MethodAttributes propAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
// プロパティを作成する
foreach (var Propertie in Properties)
{
// プライベートフィールドの作成
FieldBuilder nameFieldBuilder = typeBuilder.DefineField( Propertie.Key + "_", Propertie.Value, FieldAttributes.Private);
// パブリックプロパティ
PropertyBuilder namePropertyBuilder = typeBuilder.DefineProperty(Propertie.Key, PropertyAttributes.HasDefault, Propertie.Value, null);
#region Getterメソッドの作成
MethodBuilder getNameMethod = typeBuilder.DefineMethod("get_" + Propertie.Key, propAttr, Propertie.Value, Type.EmptyTypes);
ILGenerator getNamePropIL = getNameMethod.GetILGenerator();
getNamePropIL.Emit(OpCodes.Ldarg_0);
getNamePropIL.Emit(OpCodes.Ldfld, nameFieldBuilder);
getNamePropIL.Emit(OpCodes.Ret);
#endregion
#region Setterメソッドの作成
MethodBuilder setNameMethod = typeBuilder.DefineMethod("set_" + Propertie.Key, propAttr, null, new Type[] { Propertie.Value });
ILGenerator setNamePropIL = setNameMethod.GetILGenerator();
setNamePropIL.Emit(OpCodes.Ldarg_0);
setNamePropIL.Emit(OpCodes.Ldarg_1);
setNamePropIL.Emit(OpCodes.Stfld, nameFieldBuilder);
setNamePropIL.Emit(OpCodes.Ret);
#endregion
namePropertyBuilder.SetGetMethod(getNameMethod);
namePropertyBuilder.SetSetMethod(setNameMethod);
}
Type retval = typeBuilder.CreateType();
return retval;
}
private static void LoadInteger(ILGenerator il, int i)
{
switch (i)
{
case 0: il.Emit(OpCodes.Ldc_I4_0); break;
case 1: il.Emit(OpCodes.Ldc_I4_1); break;
case 2: il.Emit(OpCodes.Ldc_I4_2); break;
case 3: il.Emit(OpCodes.Ldc_I4_3); break;
case 4: il.Emit(OpCodes.Ldc_I4_4); break;
case 5: il.Emit(OpCodes.Ldc_I4_5); break;
case 6: il.Emit(OpCodes.Ldc_I4_6); break;
case 7: il.Emit(OpCodes.Ldc_I4_7); break;
case 8: il.Emit(OpCodes.Ldc_I4_8); break;
case -1: il.Emit(OpCodes.Ldc_I4_M1); break;
default:
if (-128 <= i && i <= 127)
{
il.Emit(OpCodes.Ldc_I4_S, i);
}
else
{
il.Emit(OpCodes.Ldc_I4, i);
}
break;
}
}
/// <summary>
/// 指定したプロパティから値を取得する
/// </summary>
/// <param name="obj">取得する対象のインスタンス</param>
/// <param name="propertyName">プロパティ名</param>
/// <param name="type">タイプ</param>
/// <returns>取得した値</returns>
public static object GetProperty(object obj, string propertyName, Type type)
{
PropertyInfo info = obj.GetType().GetProperty(propertyName, type);
if (info == null || !info.CanRead)
{
return null;
}
return info.GetValue(obj, null);
}
/// <summary>
/// 指定したプロパティに値を設定する
/// </summary>
/// <param name="obj">取得する対象のインスタンス</param>
/// <param name="propertyName">プロパティ名</param>
/// <param name="type">タイプ</param>
public static void SetProperty(object obj, string propertyName, object value, Type type)
{
PropertyInfo info = obj.GetType().GetProperty(propertyName, type);
if (info == null || !info.CanWrite)
{
return;
}
info.SetValue(obj, value, null);
}
private const string DEFALT_CLASS_NAME = "UserData";
public readonly Type type_;
#region コンストラクタ
/// <summary>コンストラクタ</summary>
/// <param name="Properties">プロパティ情報</param>
/// <example>
/// <code>
/// Dictionary<String,Type> Properties = new Dictionary<string,Type>( );
/// Properties[ "AddPropertis1" ] = typeof( Double );
/// Properties[ "AddPropertis2" ] = typeof( int );
/// Properties[ "AddPropertis3" ] = typeof( Boolean );
/// </code>
/// </example>
public CreateType(Dictionary<String, Type> Properties)
: this(DEFALT_CLASS_NAME, Properties)
{
}
public CreateType(string className, Dictionary<String, Type> Properties)
{
type_ = Create(className, Properties);
}
#endregion
public IUserData NewInstance()
{
return Activator.CreateInstance(type_) as IUserData;
}
}
コンストラクタの引数でしているする、Dictionary<String, Type>ですが
Keyにはプロパティ名を、Valueにはそのプロパティ名の型を設定してください。
そうすると、指定された型のプロパティを含むクラスを生成します。
パブリックフィールドのtype_には生成したクラスのTypeが格納されます。
使い方はXMLコメントを参照してください。
大元の情報をHoriuchi_H様には感謝を。