Following Implementation which is creating class and property dynamically:
public static class MyTypeBuilder
{
public static void CreateNewObject()
{
var myType = CompileResultType();
}
public static Type CompileResultType()
{
TypeBuilder tb = GetTypeBuilder();
ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
var fields = new List<Field>() {
new Field("EmployeeID", typeof(int)),
new Field("EmployeeName", typeof(string)),
new Field("Designation", typeof(string))
};
// NOTE: assuming your list contains Field objects with fields FieldName(string) and FieldType(Type)
foreach (var field in fields)
CreateProperty(tb, field.FieldName, field.FieldType);
Type objectType = tb.CreateType();
return objectType;
}
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;
}
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 getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
ILGenerator getIl = getPropMthdBldr.GetILGenerator();
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
getIl.Emit(OpCodes.Ret);
MethodBuilder setPropMthdBldr =
tb.DefineMethod("set_" + propertyName,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
null, new[] { propertyType });
ILGenerator setIl = setPropMthdBldr.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);
setIl.Emit(OpCodes.Nop);
setIl.MarkLabel(exitSet);
setIl.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getPropMthdBldr);
propertyBuilder.SetSetMethod(setPropMthdBldr);
}
}
In same above implementation i want to add attribute for the property
Example
[DelimitedRecord(",")]
[IgnoreEmptyLines()]
private class myDyanmicType
{
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public int EmployeeID;
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public String EmployeeName;
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public String Designation;
}
so i want to add attribute to the property like
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
how i will achieve this with my code of creating the dynamic class and property
Please help.
Just a simpler and faster approach: Write your appreciated code to a string, then compile it at runtime, and you will get everything ready!
string src = "[DelimitedRecord(\",\")] [IgnoreEmptyLines()] private class ...";
var compParms = new CompilerParameters
{
GenerateExecutable = false,
GenerateInMemory = true
};
var csProvider = new CSharpCodeProvider();
CompilerResults compilerResults = csProvider.CompileAssemblyFromSource(compParms, src);
var a = compilerResults.CompiledAssembly; // here you go
Related
I try to build runtime type builder, but i have encountered an egg and chicken problem with self referencing type property. I'm using TypeBuilder and basically what I do is I pass a list of property names and their types and i generate a type — and that works fine as long as the type is known by the time of creation. However I'd like to achieve this basic concept
public class Person
{
public string Name { get; set; }
public Person Child { get; set; }
}
And I'm pretty much stuck. How do I tell TypeBuilder that I want a property of type that has not been created yet? How CLR does this?
My current code
public class FieldDescription
{
public string FieldName { get; set; }
public Type FieldType { get; set; }
}
public static class CustomTypeBuilder
{
public static Type CompileResultType(List<FieldDescription> fields, string TypeName)
{
TypeBuilder tb = GetTypeBuilder(TypeName);
ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
foreach (var field in fields)
CreateProperty(tb, field);
Type objectType = tb.CreateType();
return objectType;
}
private static TypeBuilder GetTypeBuilder(string typeSignature)
{
var an = new AssemblyName(typeSignature);
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MyModule");
TypeBuilder tb = moduleBuilder.DefineType(typeSignature,
TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout,
null);
FieldBuilder fieldBuilder = tb.DefineField("_ToStringValue", typeof(string), FieldAttributes.Private);
return tb;
}
private static void CreateProperty(TypeBuilder tb, FieldDescription field)
{
string propertyName = field.FieldName;
Type propertyType = field.FieldType ?? tb;
FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual, propertyType, Type.EmptyTypes);
ILGenerator getIl = getPropMthdBldr.GetILGenerator();
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
getIl.Emit(OpCodes.Ret);
MethodBuilder setPropMthdBldr =
tb.DefineMethod("set_" + propertyName,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig | MethodAttributes.Virtual,
null, new[] { propertyType });
ILGenerator setIl = setPropMthdBldr.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);
setIl.Emit(OpCodes.Nop);
setIl.MarkLabel(exitSet);
setIl.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getPropMthdBldr);
propertyBuilder.SetSetMethod(setPropMthdBldr);
}
}
CreateProperty allows to pass FieldDescription field with null set for field.FieldType, in this case TypeBuilder tb type will be used as a property type.
So just pass null for FieldType and everything should work:
var compileResultType = CustomTypeBuilder.CompileResultType(new List<FieldDescription>
{
new() { FieldName = "Test1", FieldType = typeof(string) },
new() { FieldName = "Test2", FieldType = null } // property of the type being created
},
"TypeToTest");
var instance = Activator.CreateInstance(compileResultType);
var test1PI = compileResultType.GetProperty("Test1");
var test2PI = compileResultType.GetProperty("Test2");
test1PI.SetValue(instance, "test");
test2PI.SetValue(instance, instance);
Console.WriteLine(test1PI.GetValue(instance)); // prints test
Console.WriteLine(test2PI.GetValue(instance)); // prints TypeToTest
I am using code from this question to generate class from list of property
public class Field
{
public string FieldName;
public Type FieldType;
}
public static class MyTypeBuilder
{
public static Type CompileResultType()
{
TypeBuilder tb = GetTypeBuilder();
ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
// NOTE: assuming your list contains Field objects with fields FieldName(string) and FieldType(Type)
foreach (var field in yourListOfFields)
CreateProperty(tb, field.FieldName, field.FieldType);
Type objectType = tb.CreateType();
return objectType;
}
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;
}
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 getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
ILGenerator getIl = getPropMthdBldr.GetILGenerator();
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
getIl.Emit(OpCodes.Ret);
MethodBuilder setPropMthdBldr =
tb.DefineMethod("set_" + propertyName,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
null, new[] { propertyType });
ILGenerator setIl = setPropMthdBldr.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);
setIl.Emit(OpCodes.Nop);
setIl.MarkLabel(exitSet);
setIl.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getPropMthdBldr);
propertyBuilder.SetSetMethod(setPropMthdBldr);
}
}
and I have interface to get/set it's properties to avoid using reflection and dynamic
public interface IDynamicObject
{
T GetProperty<T>(string propertyName);
void SetProperty(string propertyName, object value);
}
Can anybody help me modify this code to generate a class implementing my IDynamicObject interface so it generate something like this (for example two string properties "Str1" and "Str2")? Sadly am not good with Reflection.Emit yet...
public class TestClass : IDynamicObject
{
public string Str1 { get; set; }
public string Str2 { get; set; }
public T GetProperty<T>(string propertyName)
{
switch (propertyName)
{
case nameof(Str1):
return CastObject<T>(Str1);
case nameof(Str2):
return CastObject<T>(Str2);
default: throw new ArgumentException();
}
}
public void SetProperty(string propertyName, object value)
{
switch (propertyName)
{
case nameof(Str1):
Str1 = (string)value;
break;
case nameof(Str2):
Str2 = (string)value;
break;
default: throw new ArgumentException();
}
}
public T CastObject<T>(object input)
{
return (T)input;
}
}
Here is one way how you can do it. It's a bit complicated since we are going to implement it with labels and goto logic 😱
See my comments in the code below.
public static class MyTypeBuilder
{
public static void CompileAssembly(Field[] fields)
{
const string typeSignature = "MyDynamicType";
var assemblyName = new AssemblyName(typeSignature);
var assemblyBuilder =
AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule", $"{typeSignature}.dll");
CompileResultType(moduleBuilder, typeof(IDynamicObject), fields);
// save to review the compiled assembly
assemblyBuilder.Save($"{typeSignature}.dll");
}
private static Type CompileResultType(ModuleBuilder moduleBuilder, Type interfaceType, Field[] fields)
{
var typeBuilder = moduleBuilder.DefineType(moduleBuilder.Assembly.GetName().Name,
TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass |
TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout,
null, new[] { interfaceType });
// T CastObject<T>(object input)
var castObject = typeBuilder.DefineMethod("CastObject",
MethodAttributes.Public,
null, new[] { typeof(object) });
{
var castObjectOutputParameter = castObject.DefineGenericParameters("T")[0];
castObject.SetReturnType(castObjectOutputParameter);
castObject.DefineParameter(1, ParameterAttributes.None, "input");
var il = castObject.GetILGenerator();
il.DeclareLocal(castObjectOutputParameter);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ret);
}
// create properties in advance so we can reference them later
var properties = fields.ToDictionary(
key => key.FieldName,
value => CreateProperty(typeBuilder, value.FieldName, value.FieldType)
);
// T GetProperty<T>(string propertyName)
var getProperty = typeBuilder.DefineMethod("GetProperty",
MethodAttributes.Public | MethodAttributes.Final |
MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual,
null, new[] { typeof(string) });
{
// define generic parameter T
var outputParameter = getProperty.DefineGenericParameters("T")[0];
getProperty.SetReturnType(outputParameter);
// define name for the propertyName parameter
getProperty.DefineParameter(1, ParameterAttributes.None, "propertyName");
// reference to "operator ==" to compare a field name with the propertyName value
var stringEquals =
typeof(string).GetMethod("op_Equality", BindingFlags.Static | BindingFlags.Public) ??
throw new InvalidOperationException();
var il = getProperty.GetILGenerator();
// declare local variables
il.DeclareLocal(typeof(string)); // loc_0 to store input for switch / case
il.DeclareLocal(outputParameter); // loc_1 to store result of the CastObject() call
// define general labels, we will mark their code locations later on
var returnLabel = il.DefineLabel(); // "return label"
var throwLabel = il.DefineLabel(); // "throw label" for throwing ArgumentException
// define "value labels" for each case body,
// use map to reference them later
var returnValueLabels = fields.ToDictionary(
key => key.FieldName,
value => il.DefineLabel()
);
// store propertyName in loc_0
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stloc_0);
foreach (var field in fields)
{
// check if propertyName == field.FieldName
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldstr, field.FieldName);
il.Emit(OpCodes.Call, stringEquals);
// if true, jump to the corresponding "return value" label
// we will mark a code location with it later (see next loop),
// right now we only need a reference
il.Emit(OpCodes.Brtrue, returnValueLabels[field.FieldName]);
}
// if we are here, that means the propertyName is unknown
// jump to the "throw label" location
il.Emit(OpCodes.Br, throwLabel);
foreach (var field in fields)
{
// mark the code with the corresponding "return value" label
il.MarkLabel(returnValueLabels[field.FieldName]);
// find a property we created before
// and pass its getter to the CastObject<T>() call
var property = properties[field.FieldName];
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, property.GetMethod);
il.Emit(OpCodes.Call, castObject);
// store result in loc_1
il.Emit(OpCodes.Stloc_1);
// jump to "return label"
il.Emit(OpCodes.Br, returnLabel);
}
// mark the following code that throws with "throw label"
il.MarkLabel(throwLabel);
// find ArgumentException(string) ctor
var argumentException =
typeof(ArgumentException).GetConstructor(new[] { typeof(string) }) ??
throw new InvalidOperationException();
// construct exception and throw
il.Emit(OpCodes.Ldstr, "propertyName");
il.Emit(OpCodes.Newobj, argumentException);
il.Emit(OpCodes.Throw);
// mark the following code with "return label"
il.MarkLabel(returnLabel);
// load value from loc_1
il.Emit(OpCodes.Ldloc_1);
// return
il.Emit(OpCodes.Ret);
}
// void SetProperty(string propertyName, object value)
// logic is very similar to GetProperty
var setProperty = typeBuilder.DefineMethod("SetProperty",
MethodAttributes.Public | MethodAttributes.Final |
MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual,
null, new[] { typeof(string), typeof(object) });
{
setProperty.DefineParameter(1, ParameterAttributes.None, "propertyName");
setProperty.DefineParameter(2, ParameterAttributes.None, "value");
var stringEquals =
typeof(string).GetMethod("op_Equality", BindingFlags.Static | BindingFlags.Public) ??
throw new InvalidOperationException();
var il = setProperty.GetILGenerator();
il.DeclareLocal(typeof(string));
il.DeclareLocal(typeof(string));
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stloc_0);
var returnLabel = il.DefineLabel();
var throwLabel = il.DefineLabel();
var setValueLabels = fields.ToDictionary(
key => key.FieldName,
value => il.DefineLabel()
);
foreach (var field in fields)
{
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ldstr, field.FieldName);
il.Emit(OpCodes.Call, stringEquals);
il.Emit(OpCodes.Brtrue, setValueLabels[field.FieldName]);
}
il.Emit(OpCodes.Br, throwLabel);
foreach (var field in fields)
{
var property = properties[field.FieldName];
il.MarkLabel(setValueLabels[field.FieldName]);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_2);
il.Emit(OpCodes.Castclass, field.FieldType);
il.Emit(OpCodes.Call, property.SetMethod);
il.Emit(OpCodes.Br, returnLabel);
}
il.MarkLabel(throwLabel);
var argumentException =
typeof(ArgumentException).GetConstructor(new[] { typeof(string) }) ??
throw new InvalidOperationException();
il.Emit(OpCodes.Ldstr, "propertyName");
il.Emit(OpCodes.Newobj, argumentException);
il.Emit(OpCodes.Throw);
il.MarkLabel(returnLabel);
il.Emit(OpCodes.Ret);
}
typeBuilder.DefineDefaultConstructor(
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName
);
return typeBuilder.CreateType();
}
private static PropertyBuilder 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 getPropMthdBldr = tb.DefineMethod("get_" + propertyName,
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType,
Type.EmptyTypes);
ILGenerator getIl = getPropMthdBldr.GetILGenerator();
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
getIl.Emit(OpCodes.Ret);
MethodBuilder setPropMthdBldr =
tb.DefineMethod("set_" + propertyName,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
null, new[] { propertyType });
ILGenerator setIl = setPropMthdBldr.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);
setIl.Emit(OpCodes.Nop);
setIl.MarkLabel(exitSet);
setIl.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getPropMthdBldr);
propertyBuilder.SetSetMethod(setPropMthdBldr);
return propertyBuilder;
}
}
Here is how it works
var fields = new[]
{
new Field { FieldName = "Str1", FieldType = typeof(string) },
new Field { FieldName = "Str2", FieldType = typeof(string) },
new Field { FieldName = "Str3", FieldType = typeof(string) }
};
MyTypeBuilder.CompileAssembly(fields);
...will generate the following:
public class MyDynamicType : IDynamicObject
{
// ... getters and setters
public T CastObject<T>(object input)
{
return (T)input;
}
public T GetProperty<T>(string propertyName)
{
switch (propertyName)
{
case "Str1":
return CastObject<T>(this.Str1);
case "Str2":
return CastObject<T>(this.Str2);
case "Str3":
return CastObject<T>(this.Str3);
default:
throw new ArgumentException("propertyName");
}
}
public void SetProperty(string propertyName, object value)
{
switch (propertyName)
{
case "Str1":
Str1 = (string)value;
break;
case "Str2":
Str2 = (string)value;
break;
case "Str3":
Str3 = (string)value;
break;
default:
throw new ArgumentException("propertyName");
}
}
}
I have an ML.NET application where I have to create interface IDataView dynamically after compile time to be used for training. I found this thread and I've been able to successfully create a dynamic interface for the training data set and then use it to train a model. My problem comes in when I try to use that same interface in order to create a prediction using that trained model. The docs show that you should create a prediction engine where you have to define both the input and output class types in order to create the engine. Something like:
mlContext.Model.CreatePredictionEngine<TSrc,TDst>(ITransformer, DataViewSchema)
where TSrc and TDst are class types that are known at compile time. My problem is that I don't know the structure of the input class type at compile time and have to create a dynamic interface for the input data source. The output class object can be defined since the parameters are known, but I'm unsure how to proceed with a dynamic input.
I thought I could maybe try to use something like GetType() on the interface but it says that "implicitly-typed variables cannot have multiple declarators". My simplified example looks like this:
public class ModelOutput
{
public string PredictedLabel { get; set; }
public float[] Score { get; set; }
}
public class MakePrediction
{
protected void Solve(IDataView data, ITransformer model)
{
var mlContext = new MLContext();
var engine = mlContext.Model.CreatePredictionEngine<data.GetType(), ModelOutput>(model, data.Schema);
}
}
It's possible to generate a runtime class that has all the fields listed in the DataViewSchema. This will allow you to create a PredictionEngine.
You won't be able to create the PredictionEngine directly, you'll have to invoke it. Here is some sample code:
// Create runtime type from fields and types in a DataViewSchema
var runtimeType = ClassFactory.CreateType(dataViewSchema);
dynamic dynamicPredictionEngine;
var genericPredictionMethod = mlContext.Model.GetType().GetMethod("CreatePredictionEngine", new[] { typeof(ITransformer), typeof(DataViewSchema) });
var predictionMethod = genericPredictionMethod.MakeGenericMethod(runtimeType, typeof(PricePrediction));
dynamicPredictionEngine = predictionMethod.Invoke(mlContext.Model, new object[] { model, dataViewSchema });
To actually use the PredictionEngine (dynamicPredictionEngine), use a call similar to this:
var predictMethod = dynamicPredictionEngine.GetType().GetMethod("Predict", new[] { runtimeType });
var predict = predictMethod.Invoke(dynamicPredictionEngine, new[] { inputObject });
I used modified copy (ClassFactory above) of the source from this wonderful example of creating runtime classes. My copy accepts a DataViewSchema to auto generate the requires class. That code is below:
using Microsoft.ML;
using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
public static class ClassFactory
{
private static AssemblyName _assemblyName;
public static object CreateObject(string[] PropertyNames, Type[] Types)
{
_assemblyName = new AssemblyName("DynamicInput");
if (PropertyNames.Length != Types.Length)
{
Console.WriteLine("The number of property names should match their corresponding types number");
}
TypeBuilder DynamicClass = CreateTypeBuilder();
CreateConstructor(DynamicClass);
for (int ind = 0; ind < PropertyNames.Count(); ind++)
CreateProperty(DynamicClass, PropertyNames[ind], Types[ind]);
Type type = DynamicClass.CreateType();
return Activator.CreateInstance(type);
}
public static Type CreateType(DataViewSchema dataViewSchema)
{
_assemblyName = new AssemblyName("DynamicInput");
TypeBuilder DynamicClass = CreateTypeBuilder();
CreateConstructor(DynamicClass);
foreach (var item in dataViewSchema)
{
CreateProperty(DynamicClass, item.Name, item.Type.RawType);
}
return DynamicClass.CreateType();
}
private static TypeBuilder CreateTypeBuilder()
{
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(_assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
TypeBuilder typeBuilder = moduleBuilder.DefineType(_assemblyName.FullName
, TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout
, null);
return typeBuilder;
}
private static void CreateConstructor(TypeBuilder typeBuilder)
{
typeBuilder.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
}
private static void CreateProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType)
{
FieldBuilder fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
MethodBuilder getPropMthdBldr = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
ILGenerator getIl = getPropMthdBldr.GetILGenerator();
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
getIl.Emit(OpCodes.Ret);
MethodBuilder setPropMthdBldr = typeBuilder.DefineMethod("set_" + propertyName,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
null, new[] { propertyType });
ILGenerator setIl = setPropMthdBldr.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);
setIl.Emit(OpCodes.Nop);
setIl.MarkLabel(exitSet);
setIl.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getPropMthdBldr);
propertyBuilder.SetSetMethod(setPropMthdBldr);
}
}
Look up MethodInfo.MakeGenericMethod()
I'm writing an interface implementator for simple interfaces like
interface IPoint
{
int X { get; }
int Y { get; }
}
It almost works, but when I try to implement any property I get an error
Signature of the body and declaration in a method implementation do not match
I don't understand why Emit thinks that properties does not match.
Here is code sample:
private static class InterfaceImplementator<T> where T: class
{
[SuppressMessage("ReSharper", "StaticMemberInGenericType")]
public static Type Value { get; }
static InterfaceImplementator()
{
var interfaceType = typeof(T);
if (!interfaceType.IsInterface)
{
throw new ArgumentException($"{interfaceType.FullName} should be an interface!");
}
var interfaceProps = interfaceType.GetProperties();
if (interfaceType.GetMethods().Except(interfaceProps.Select(x => x.GetMethod).Concat(interfaceProps.Select(x => x.SetMethod))).Any())
{
throw new ArgumentException($"{interfaceType.FullName} must have properties only!");
}
var tb = Builder.DefineType($"<{interfaceType.Name}>__Implementation", TypeAttributes.Class | TypeAttributes.Sealed);
foreach (var interfaceProp in interfaceProps)
{
var prop = tb.EmitAutoProperty(interfaceProp.Name, interfaceProp.PropertyType);
if (interfaceProp.CanRead)
{
tb.DefineMethodOverride(prop.GetMethod, interfaceProp.GetMethod);
}
if (interfaceProp.CanWrite)
{
tb.DefineMethodOverride(prop.SetMethod, interfaceProp.SetMethod);
}
}
tb.AddInterfaceImplementation(interfaceType);
Value = tb.CreateType();
}
}
where EmitProperty:
public static PropertyInfo EmitAutoProperty(this TypeBuilder tb, string propertyName, Type propertyType)
{
var backingField = tb.DefineField($"<{propertyName}>k__BackingField", propertyType, FieldAttributes.Private);
var propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
var getMethod = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig);
var getGenerator = getMethod.GetILGenerator();
getGenerator.Emit(OpCodes.Ldarg_0);
getGenerator.Emit(OpCodes.Ldfld, backingField);
getGenerator.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getMethod);
var setMethod = tb.DefineMethod("set_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig);
var setGenerator = setMethod.GetILGenerator();
setGenerator.Emit(OpCodes.Ldarg_0);
setGenerator.Emit(OpCodes.Ldarg_1);
setGenerator.Emit(OpCodes.Stfld, backingField);
setGenerator.Emit(OpCodes.Ret);
propertyBuilder.SetSetMethod(setMethod);
return propertyBuilder;
}
Try using the 4-arg DefineMethod call for the get_ and set_ methods so you can define the return type / arg:
var getMethod = tb.DefineMethod("get_" + propertyName,
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
propertyType,
Type.EmptyTypes);
var setMethod = tb.DefineMethod("set_" + propertyName,
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
null,
new [] { propertyType });
I need to generate an interface.
I'va a problem to generate (emit) the virtual properties. It seems they are not generated.
I figure out I'm doing something wrong:
private static TypeBuilder getTypeBuilder()
{
var typeSignature = "DynamicDigitalInput";
var an = new AssemblyName(typeSignature);
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicDomain");
TypeBuilder tb = moduleBuilder.DefineType(typeSignature
, TypeAttributes.Public |
TypeAttributes.Interface |
TypeAttributes.Abstract |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout
, null);
return tb;
}
private static void createProperty(TypeBuilder tb, string propertyName, Type propertyType)
{
PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
}
In order to generate the interface:
TypeBuilder tb = getTypeBuilder();
createProperty(tb, "p1", String.GetType());
createProperty(tb, "p2", Int32.GetType());
When I perform this:
Type i = tb.CreateType();
System.Reflection.PropertyInfo p1 = type.GetProperty("p1");
p1 is null.
What am I doing wrong?
The property is not defined correctly. In order for GetProperty to work, the property must have at least one public getter or setter. Right now, is does not have even one getter or setter, so they never can be public.
So, you have to create a public get-method and/or a public set-method (using the MethodBuilder). Try this:
private static void createProperty(TypeBuilder tb, string propertyName, Type propertyType)
{
PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
MethodBuilder methodBuilder = tb.DefineMethod("get_" + propertyName, MethodAttributes.Virtual | MethodAttributes.Abstract | MethodAttributes.Public);
propertyBuilder.SetGetMethod(methodBuilder);
}