2017-01-04 10 views
1

実行時に動的にc#クラスを作成しようとしています。実行時にC#で動的タイプを作成

using System; 

class Hist 
{ 
    private int? _min; 
    private int? _max; 

    public int? min 
    { 
     get{return _min;} 
     set {_min = value;} 
    } 

    public int? max 
    { 
     get{return _max;} 
     set {_max = value;} 
    } 
} 

public class ProcessData 
{ 
    private string _id; 
    private string _source; 
    private int? _currentValue; 
    private Hist _hist; 

    public Hist hist 
    { 
     get { return _hist; } 
     set{ _hist = value; } 
    } 

    public string id 
    { 
     get {return _id;} 
     set { _id = value; } 
    } 

    public string source 
    { 
     get {return _source;} 
     set { _source = value; } 
    } 

    public int? currentValue 
    { 
     get {return _currentValue;} 
     set { _currentValue = value; } 
    } 

    public int? min 
    { 
     get { return (hist != null) ? hist.min : null; }   
    } 
    public int? max 
    { 
     get { return (hist != null) ? hist.max : null; }   
    } 
} 

これは特にできません。私はちょうど必要

return (hist != null) ? hist.max : null; 

ProcessDataクラスのminまたはmaxプロパティのいずれかのためのgetメソッドです。

上記のタスクのための私のコード:

var method = parentType.GetMethod("get_" + propertyName); 
getPropMthdBldr = tb.DefineMethod("get_" + propertyName, 
     MethodAttributes.Public | 
     MethodAttributes.SpecialName | 
     MethodAttributes.HideBySig, 
    propertyType, Type.EmptyTypes); 
getIl = getPropMthdBldr.GetILGenerator(); 
var moveTo = getIl.DefineLabel(); 
getIl.Emit(OpCodes.Ldarg_0); 
getIl.EmitCall(OpCodes.Call, parentGetMethod, Type.EmptyTypes); 
getIl.Emit(OpCodes.Brtrue_S, moveTo); 
getIl.Emit(OpCodes.Ldloca_S, 0); 
getIl.Emit(OpCodes.Initobj, typeof(int?)); 
getIl.Emit(OpCodes.Ldloc_0); 
getIl.Emit(OpCodes.Ret); 
getIl.MarkLabel(moveTo); 
getIl.Emit(OpCodes.Ldarg_0); 
getIl.EmitCall(OpCodes.Call, parentGetMethod,Type.EmptyTypes); 
getIl.EmitCall(OpCodes.Callvirt, method,Type.EmptyTypes); 
getIl.Emit(OpCodes.Ret); 
+0

TBは何か?親タイプは何ですか?プロパティタイプとは何ですか?最小、完全、および検証可能なサンプルを提供できますか? –

+0

tbはTypeBuilder、parentTypeはtypeof(Hist)、propertyTypeはtypeof(int?)です。 – Narender

答えて

0

問題は、あなたが宣言されていないローカル変数を使用しようとしているということです。

getIl.Emit(OpCodes.Ldloca_S, 0);   // load address of local variable with index 0 on stack 
getIl.Emit(OpCodes.Initobj, typeof(int?)); // initialize local variable 
getIl.Emit(OpCodes.Ldloc_0);    // load value of local variable with index 0 on stack 

あなたはこのような必要なローカル変数を定義することができます

var local = getIl.DeclareLocal(typeof(int?)); 

あなたのコードは有効ですが、読みやすさを向上させるために、私はあなたにvariab代わりにlocalのインデックス。これは次のようにすることができます:

getIl.Emit(OpCodes.Ldloca_S, local);  // load address of local variable on stack 
getIl.Emit(OpCodes.Initobj, typeof(int?)); // initialize local variable 
getIl.Emit(OpCodes.Ldloc, local);   // load value of local variable on stack 

P.S.クラスHistProcessDataの生成に使用したコードをここに記入します。私の説明が十分でない場合には、何らかの形で役に立つかもしれません。

プロパティを作成するため、このヘルパーでメインロジック:ここ

public static class TypeBuilderExtensions 
{ 
    public static PropertyBuilder CreateProperty<T>(this TypeBuilder builder, string name) => CreateProperty(builder, typeof(T), name); 

    public static PropertyBuilder CreateProperty(this TypeBuilder builder, Type propertyType, string name) 
    { 
     var field = builder.DefineField($"_{name}", propertyType, FieldAttributes.Private); 
     var getMethodBuilder = builder.DefineMethod($"get_{name}", MethodAttributes.Public, propertyType, Type.EmptyTypes); 
     var getGenerator = getMethodBuilder.GetILGenerator(); 
     getGenerator.Emit(OpCodes.Ldarg_0); 
     getGenerator.Emit(OpCodes.Ldfld, field); 
     getGenerator.Emit(OpCodes.Ret); 

     var setMethodBuilder = builder.DefineMethod($"set_{name}", MethodAttributes.Public, typeof(void), new[] { propertyType }); 
     var setGenerator = setMethodBuilder.GetILGenerator(); 
     setGenerator.Emit(OpCodes.Ldarg_0); 
     setGenerator.Emit(OpCodes.Ldarg_1); 
     setGenerator.Emit(OpCodes.Stfld, field); 
     setGenerator.Emit(OpCodes.Ret); 

     var propertyBuilder = builder.DefineProperty(name, PropertyAttributes.None, propertyType, Type.EmptyTypes); 
     propertyBuilder.SetGetMethod(getMethodBuilder); 
     propertyBuilder.SetSetMethod(setMethodBuilder); 
     return propertyBuilder; 
    } 

    public static PropertyBuilder CreateCalculatedProperty<T>(this TypeBuilder builder, string name, MethodInfo getObject, MethodInfo getObjectProperty) => CreateCalculatedProperty(builder, typeof(T), name, getObject, getObjectProperty); 

    public static PropertyBuilder CreateCalculatedProperty(this TypeBuilder builder, Type propertyType, string name, MethodInfo getObject, MethodInfo getObjectProperty) 
    { 
     var getMethodBuilder = builder.DefineMethod($"get_{name}", MethodAttributes.Public, propertyType, Type.EmptyTypes); 
     var getGenerator = getMethodBuilder.GetILGenerator(); 
     var label = getGenerator.DefineLabel(); 
     var local = getGenerator.DeclareLocal(propertyType); 
     getGenerator.Emit(OpCodes.Ldarg_0); 
     getGenerator.Emit(OpCodes.Callvirt, getObject); 
     getGenerator.Emit(OpCodes.Brtrue, label); 
     getGenerator.Emit(OpCodes.Ldloca_S, local); 
     getGenerator.Emit(OpCodes.Initobj, propertyType); 
     getGenerator.Emit(OpCodes.Ldloc, local); 
     getGenerator.Emit(OpCodes.Ret); 
     getGenerator.MarkLabel(label); 
     getGenerator.Emit(OpCodes.Ldarg_0); 
     getGenerator.Emit(OpCodes.Callvirt, getObject); 
     getGenerator.Emit(OpCodes.Callvirt, getObjectProperty); 
     getGenerator.Emit(OpCodes.Ret); 

     var propertyBuilder = builder.DefineProperty(name, PropertyAttributes.None, propertyType, Type.EmptyTypes); 
     propertyBuilder.SetGetMethod(getMethodBuilder); 
     return propertyBuilder; 
    } 
} 

は、クラスの作成そのものです:

var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("TestAssembly"), AssemblyBuilderAccess.Run); 
var moduleBuilder = assemblyBuilder.DefineDynamicModule("TestModule"); 
var histBuilder = moduleBuilder.DefineType("Hist"); 
var minProperty = histBuilder.CreateProperty<int?>("min"); 
var maxProperty = histBuilder.CreateProperty<int?>("max"); 

var processDataBuilder = moduleBuilder.DefineType("ProcessData"); 
var histProperty = processDataBuilder.CreateProperty(histBuilder, "hist"); 
processDataBuilder.CreateProperty<string>("id"); 
processDataBuilder.CreateProperty<string>("source"); 
processDataBuilder.CreateProperty<int?>("currentValue"); 

processDataBuilder.CreateCalculatedProperty<int?>("min", histProperty.GetMethod, minProperty.GetMethod); 
processDataBuilder.CreateCalculatedProperty<int?>("max", histProperty.GetMethod, maxProperty.GetMethod); 

し、作成したクラスの最後に原始的検証:

void ValidateProperty(object instance, string name, object value, bool setValue = true) 
{ 
    var type = instance.GetType(); 
    var property = type.GetProperty(name); 
    if (setValue) property.SetValue(instance, value); 
    var result = property.GetValue(instance); 

    var equals = property.PropertyType.IsValueType && value != null ? value.Equals(result) : value == result; 
    if (!equals) 
     throw new InvalidDataException("Property not valid"); 
} 

var histType = histBuilder.CreateType(); 
var histInstance = Activator.CreateInstance(histType); 
ValidateProperty(histInstance, "min", 12); 
ValidateProperty(histInstance, "max", 21); 

var processDataType = processDataBuilder.CreateType(); 
var processDataInstance = Activator.CreateInstance(processDataType); 
ValidateProperty(processDataInstance, "hist", histInstance); 
ValidateProperty(processDataInstance, "id", "Test!"); 
ValidateProperty(processDataInstance, "source", "Source#"); 
ValidateProperty(processDataInstance, "currentValue", 126); 

ValidateProperty(processDataInstance, "min", 12, false); 
ValidateProperty(processDataInstance, "max", 21, false); 

ValidateProperty(processDataInstance, "hist", null); 
ValidateProperty(processDataInstance, "min", null, false); 
ValidateProperty(processDataInstance, "max", null, false); 
関連する問題