This is the first time I'm dabbling with generated CIL, so please bear with my ignorance. I'm looking for a simple DynamicMethod that can read the fields of a POCO, and fill them into an object[]. No type conversion is necessary. I've put together everything I can, can you help complete it?
Type t = typeof(POCO);
DynamicMethod dm = new DynamicMethod("Get" + memberName,typeof(MemberType), new Type[] { objectType }, objectType);
ILGenerator il = dm.GetILGenerator();
// Load the instance of the object (argument 0) onto the stack
il.Emit(OpCodes.Ldarg_0);
// get fields
FieldInfo[] fields = t.GetFields();
// how do I create an array (object[]) at this point?
// per field
foreach (var pi in fields) {
// Load the value of the object's field (fi) onto the stack
il.Emit(OpCodes.Ldfld, fi);
// how do I add it into the array?
}
// how do I push the array onto the stack?
// return the array
il.Emit(OpCodes.Ret);
You can use this code to generate a compiled lambda expression.
public static Func<T, object[]> MakeFieldGetter<T>() {
var arg = Expression.Parameter(typeof(T), "arg");
var body = Expression.NewArrayInit(
typeof(object)
, typeof(T).GetFields().Select(f => (Expression)Expression.Convert(Expression.Field(arg, f), typeof(object)))
);
return (Func<T, object[]>)Expression
.Lambda(typeof(Func<T, object[]>), body, arg)
.Compile();
}
This is equivalent to the following manually written code:
object[] GetFields(MyClass arg) {
return new object[] {
// The list of fields is generated through reflection
// at the time of building the lambda. There is no reflection calls
// inside the working lambda, though: the field list is "baked into"
// the expression as if it were hard-coded manually.
(object)arg.Field1
, (object)arg.Field2
, (object)arg.Field3
};
}
This code also produces IL, but instead of you writing it manually, it lets Lambda's Compile method do it for your.
Here is a working demo on ideone.
Related
I would like to instantiate a (non-generic) class in C# (ClassToBeInstantiated) that has a public non-parameterless constructor via LINQ Expression.New inside an Expression.Block.
see update below, based on answer from #sweeper
Description
According to the documentation, the only overloads for Expression.New that do accept arguments require a ConstructorInfo argument. As I do not have access to that info beforehand, but have to retrieve this inside the Expression.Block.
So I am able to use an Expression.Call on the type ClassToBeInstantiated that is passed into the block.
However, all Expression.New overloads only accept either ConstructorInfo as an argument or for an instantiation if I want to pass arguments to the constructor. Type is only available for using a parameterless constructor.
I cannot use a ParameterExpression holding a ConstructorInfo either.
Question
So my question: is there a way around this with Expression.New? I know I can use ConstructorInfo.Invoke via another Expression.Call. But this seems akward to me, as -at least in my opinion- the Expression.New API should exactly do this for me.
Am I missing something?
Thanks for your help, comments and reply.
The class to be instantiated
Here is some additional information to further illustrate the case:
public class ClassToBeInstantiated
{
public ClassToBeInstantiated(int count) { }
}
Helper class to retrieve ConstructorInfo
public static class GetConstructor
{
public static ConstructorInfo GetNonGenericConstructorInfo(Type type, params Type[] typeArguments) =>
type.GetConstructor(typeArguments);
}
[Test]
public void InstantiateTypeViaExpressionNew()
{
var typeExpression = Expression.Parameter(typeof(Type), "type");
var countExpression = Expression.Parameter(typeof(int), "count");
var ctorExpression = Expression.Variable(typeof(ConstructorInfo), "constructorInfo");
var block = Expression.Block
(
new[] { ctorExpression },
Expression.Assign(ctorExpression, Expression.Call(typeof(GetConstructor), nameof(GetConstructor.GetNonGenericConstructorInfo),
Type.EmptyTypes, typeExpression, Expression.Constant(new[] { countExpression.Type }))),
Expression.New(/* error - obviously does not compile: ctorInfo */, countExpression)
);
var lambda = Expression.Lambda<Func<Type, int, object>>(block, typeExpression, countExpression);
var func = lambda.Compile();
var o = func.Invoke(typeof(ClassToBeInstantiated), 4);
var instance = o as ClassToBeInstantiated;
Assert.NotNull(instance);
}
Update with Expression.Call instead of Expression.New
I updated the code example from my original question, based on the answer from #Sweeper to provide a full example (in case someone is interested):
Updated helper class
Here, I added the field constructorInfoInvokeMethodInfo for retrieval of the MethodInfo for the ConstructorInfo.Invoke() method (to be called from inside the Expression.Block:
public static class GetConstructor
{
public static ConstructorInfo GetNonGenericConstructorInfo(Type type, params Type[] typeArguments) =>
type.GetConstructor(typeArguments);
public static readonly MethodInfo constructorInfoInvokeMethodInfo =
typeof(ConstructorInfo).GetMethod(nameof(ConstructorInfo.Invoke), new[] { typeof(object[]) });
}
Updated test for Expression.Block
Here, I replaced the (non-working) Expression.New with an Expression.Call to instantiate the type via ConstructorInfo.Invoke():
[Test]
public void InstantiateTypeViaExpressionCall()
{
var typeExpression = Expression.Parameter(typeof(Type), "type");
var countExpression = Expression.Parameter(typeof(int), "count");
var ctorExpression = Expression.Variable(typeof(ConstructorInfo), "constructorInfo");
var block = Expression.Block
(
new[] { ctorExpression },
Expression.Assign
(
ctorExpression,
Expression.Call(typeof(Activator), nameof(GetNonGenericConstructorInfo), Type.EmptyTypes, typeExpression, Expression.Constant(new[] { countExpression.Type }))
),
Expression.Call(ctorExpression, constructorInfoInvokeMethodInfo, Expression.NewArrayInit(typeof(object), Expression.Convert(countExpression, typeof(object))))
);
var lambda = Expression.Lambda<Func<Type, int, object>>(block, typeExpression, countExpression);
var func = lambda.Compile();
var o = func.Invoke(typeof(ClassToBeInstantiated), 4);
var instance = o as ClassToBeInstantiated;
Assert.NotNull(instance);
}
So my question: is there a way around this with Expression.New? I know I can use ConstructorInfo.Invoke via another Expression.Call.
This is exactly what you should do.
Think about the expression tree that you want to create. Since the final usage that you want to achieve looks like:
func.Invoke(typeof(ClassToBeInstantiated), 4);
func's expression must look like:
(type, count) => GetConstructor.GetNonGenericConstructorInfo(type, typeof(int)).Invoke(new object[] {count})
This seems to be what you were trying to do too, just with an extra local variable.
It cannot be:
(type, count) => new type(count)
Because new type(count) is not a valid expression. type is just a parameter.
This has nothing to do with new expressions that Expression.New creates. There are no NewExpressions in your desired expression at all. It would be very weird if Expression.New suddenly starts inserting ConstructorInfo.Invoke calls in the expression tree, because that is not what it is supposed to create at all.
You could use Expression.New here to create expressions such as:
(type, count) => new ClassToBeInstantiated(count)
But I don't think that's what you want... You want the type parameter to determine what type to instantiate, and assume that a one-parameter int constructor to be available, right?
Using sharplab.io, you can write the lambda expression you want, and see what code the compiler actually generates for building the expression tree. After cleaning up the code that generates for
Expression<Func<Type, int, object>> func =
(type, count) => GetConstructor.GetNonGenericConstructorInfo(type, typeof(int)).Invoke(new object[] {count});
You get:
MethodInfo getConstructorMethod = typeof(GetConstructor).GetMethod(nameof(GetConstructor.GetNonGenericConstructorInfo));
MethodInfo invokeMethod = typeof(ConstructorInfo).GetMethod(nameof(ConstructorInfo.Invoke), new Type[] { typeof(object[]) });
ParameterExpression typeParam = Expression.Parameter(typeof(Type), "type");
ParameterExpression countParam = Expression.Parameter(typeof(int), "count");
// GetNonGenericConstructorInfo(type, typeof(int))
MethodCallExpression instance = Expression.Call(null, getConstructorMethod,
typeParam, Expression.NewArrayInit(typeof(Type), Expression.Constant(typeof(int), typeof(Type)))
);
// // .Invoke(new object[] { count })
MethodCallExpression body = Expression.Call(instance, invokeMethod,
Expression.NewArrayInit(typeof(object), Expression.Convert(countParam, typeof(object)))
);
Expression<Func<Type, int, object>> func = Expression.Lambda<Func<Type, int, object>>(body, typeParam, countParam);
And func.Compile()(typeof(ClassToBeInstantiated), 4) as ClassToBeInstantiated is not null.
I want to make a DynamicMethod which in turn behave like the following for example:
AnyClass myClass = new AnyClass();
Func<AnyClass> myFunc = () => myClass;
I know that when I want to make it work with an int instead of AnyClass I have to use the following snippet to return every time the number 12:
// Define method
DynamicMethod method = new DynamicMethod(
"Name",
typeof(int),
new Type[] { });
// Define method body
ILGenerator il = method.GetILGenerator();
il.Emit(OpCodes.Ldc_I4, 12);
il.Emit(OpCodes.Ret);
But now I ask myself how to do it with a not build-in class.
Edit:
At the end I want to get a reference from a local variable when I call the DynamicMethod. Look at the following snippet to better understand what I wanted to archieve. Here I want a func which returns on every call the variable I passed to the function which creates the func.
Func<AnyClass> GetMethodWithAFunc(AnyClass myClass) {
Func<AnyClass> myFunc = () => myClass;
return myFunc;
}
The generated IL Code for the snippet can be found on SharpLab. Unfortunately we have to provide a context where the data to return in the DynamicMethod could be saved. Finally I suggest a static cache and to use converting and unboxing when returning the values.
I want to return an Expression.Call that creates a dense MathNet Matrix.
This is the Matrix I want:
Matrix<ContentType>.Build.Dense(Rows,Columns)
ContentType will be int, double or Complex.
But I want to create it using Expression.Call.
Here's my current code:
Expression.Call(
typeof(Matrix<>)
.MakeGenericType(ContentType)
.GetProperty("Build")
.GetMethod("Dense", new[] {typeof(int), typeof(int)}),
Expression.Constant(Rows), Expression.Constant(Columns));
This however results in a build error:
[CS1955] Non-invocable member 'PropertyInfo.GetMethod' cannot be used like a method.
What am I doing wrong?
There is GetMethod property on PropertyInfo type, which returns property getter method. You are trying to use this property as a method (invoke it) - hence the compiler error. Instead you should do it like this:
// first get Build static field (it's not a property by the way)
var buildProp = typeof(Matrix<>).MakeGenericType(ContentType)
.GetField("Build", BindingFlags.Public | BindingFlags.Static);
// then get Dense method reference
var dense = typeof(MatrixBuilder<>).MakeGenericType(ContentType)
.GetMethod("Dense", new[] { typeof(int), typeof(int) });
// now construct expression call
var call = Expression.Call(
Expression.Field(null /* because static */, buildProp),
dense,
Expression.Constant(Rows),
Expression.Constant(Columns));
I have a fully initialized MethodBuilder and EnumBuilder. The MethodBuilder points to the entry point of a dynamic assembly. It has the following signature:
public static int Main (string [] args) {...}
The assembly generation code works fine and I can use Reflection.Emit to test it. Instead of emitting IL, I want to save the target code from an expression tree. It should:
declare a enum variable
assign it a value
write to the console
read pause the console
return a enum value as an Int32
EXPRESSION TREE:
// Intention: Declare string [] args in the expression scope.
var arguments = Expression.Parameter(typeof(string []), "args");
// Intention: var code = default(MyDynamicEnum);
var code = Expression.Variable(builderEnum, "code");
// Intention: code = MyDynamicEnum.Two;
var assign = Expression.Assign(code, Expression.Constant(2, builderEnum));
// Intention: Console.WriteLine(args [0]);
var write = Expression.Call(typeof(Console).GetMethod("WriteLine", new Type [] { typeof(string) }), Expression.ArrayIndex(arguments, Expression.Constant(0, typeof(int))));
// Intention: Console.ReadKey(true);
var read = Expression.Call(typeof(Console).GetMethod("ReadKey", new Type [] { typeof(bool) }), Expression.Constant(true, typeof(bool)));
// Intention: return ((int) code);
var #return = Expression.Constant(2, typeof(int));
// How to combine above expressions and create a function body?
var block = Expression.Block(arguments, code, assign, write, read, #return);
var lambda = Expression.Lambda<Func<string [], int>>(block, new ParameterExpression [] { arguments });
lambda.CompileToMethod(builderMethod); // Error: Variable 'code' of type 'Type: MyDynamicEnum' referenced from scope '', but it is not defined.
The full code is available on this GIST. The error on the last line seems to make sense but I don't know how to fix it. The enumeration MyDynamicEnum is already created as a type but how do I import it into the expression tree context? Any pointers would be appreciated.
Solved it by using the correct overload of Expression.Block.
In order to use variables in the expression scope, we have to specify:
Expression.Block(variables.ToArray(), queue);
where variables is an array of ParameterExpression types.
I was going over a Joel Pobar's Dodge Common Performance Pitfalls to Craft Speedy Applications article on Reflection and I was looking at a particular piece of code that isn't compiling (slightly modified to narrow down to the specific error, because his example had more errors):
MethodInfo writeLine = typeof(Console).GetMethod("WriteLine");
RuntimeMethodHandle myMethodHandle = writeLine.MethodHandle;
DynamicMethod dm = new DynamicMethod(
"HelloWorld", // name of the method
typeof(void), // return type of the method
new Type[]{}, // argument types for the method
false); // skip JIT visibility checks
ILGenerator il = dm.GetILGenerator();
il.Emit(OpCodes.Ldstr, "Hello, world");
il.Emit(OpCodes.Call, myMethodHandle); // <-- 2 errors here
il.Emit(OpCodes.Ret);
The errors are:
Program.cs(350,13): error CS1502: The best overloaded method match for 'System.Reflection.Emit.ILGenerator.Emit(System.Reflection.Emit.OpCode, byte)' has some invalid arguments
Program.cs(350,35): error CS1503: Argument '2': cannot convert from 'System.RuntimeMethodHandle' to 'byte'
The ILGenerator can Emit with a MethodInfo, but it doesn't seem to support MethodHandle... does anybody know how to get this sample to work?
Like so?
MethodInfo writeLine = typeof(Console).GetMethod("WriteLine", new Type[] {typeof(string)});
RuntimeMethodHandle myMethodHandle = writeLine.MethodHandle;
DynamicMethod dm = new DynamicMethod(
"HelloWorld", // name of the method
typeof(void), // return type of the method
new Type[] { }, // argument types for the method
false); // skip JIT visibility checks
ILGenerator il = dm.GetILGenerator();
il.Emit(OpCodes.Ldstr, "Hello, world");
il.EmitCall(OpCodes.Call, writeLine, null);
il.Emit(OpCodes.Ret);
// test it
Action act = (Action)dm.CreateDelegate(typeof(Action));
act();
Changes:
I used a tweaked GetMethod to find the (string) overload (otherwise it is an ambiguous match)
use the MethodInfo, not the handle (since that is what ILGenerator wants)
use EmitCall (the other might have worked too, but I know this way works)
Through this library on Nuget.org:
ClassWrapper
It creates wrappers for types that internally uses dynamically generated methods. So no reflection used (only into the ClassWrapperDescriptor.Load method to generate the dynamic expressions)
Given, e.g. e class named SampleClass
//Create the class wrapper descriptor, this can be cached and reused!
var classWrapperDescriptor = new ClassWrapperDescriptor(typeof(SampleClass));
//Initialize the descriptor
classWrapperDescriptor.Load();
//Create the instance of our object
object instance = new SampleClass();
//Create an instance of the wrapper
var classWrapper = classWrapperDescriptor.CreateWrapper(instance);
//Set a property
classWrapper.Set("StringProperty","test");
//Get a property
var stringPropertyValue = classWrapper.Get<string>("StringProperty");
//Invoke a method without return statement
classWrapper.Invoke("ParamMethod", "paramValue");
//Invoke a method witho return statement
var result = classWrapper.Invoke<int>("ReturnMethod", "paramValue");
Any feedback on this library is really appreciated!