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);
}