Je voudrais instancier une classe (non générique) en C# ( ClassToBeInstantiated
) qui a une non -Constructeur sans paramètre via LINQ. Expression.New
à l'intérieur d'un Expression.Block
.
voir la mise à jour ci-dessous, basée sur la réponse de @sweeper
Description
D'après la documentation, les seules surcharges de la fonction Expression.New
qui acceptent les arguments nécessitent un ConstructorInfo
argument. Comme je n'ai pas accès à cette information au préalable, mais que je dois la récupérer à l'intérieur de l'application Expression.Block
. Je suis donc en mesure d'utiliser un Expression.Call
sur le type
ClassToBeInstantiated
qui est passé dans le bloc.
Cependant, tous les Expression.New
Les surcharges n'acceptent que ConstructorInfo
comme argument ou pour une instanciation si je veux passer des arguments au constructeur. Type
est uniquement disponible pour l'utilisation d'un sans paramètre constructeur.
Je ne peux pas utiliser un ParameterExpression
tenant un ConstructorInfo
soit.
Question
Ma question est donc la suivante : existe-t-il un moyen de contourner ce problème avec Expression.New
? Je sais que je peux utiliser ConstructorInfo.Invoke
via un autre Expression.Call
. Mais cela me semble maladroit, car - du moins à mon avis - la Expression.New
L'API devrait exactement faire ça pour moi.
Est-ce que j'ai manqué quelque chose ?
Merci pour votre aide, vos commentaires et votre réponse.
La classe à instancier
Voici quelques informations supplémentaires pour illustrer davantage le cas :
public class ClassToBeInstantiated
{
public ClassToBeInstantiated(int count) { }
}
Classe d'aide pour récupérer 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);
}
Mise à jour avec Expression.Call
au lieu de Expression.New
J'ai mis à jour l'exemple de code de ma question originale, basé sur la réponse de @Sweeper pour fournir un exemple complet (au cas où quelqu'un serait intéressé) :
Mise à jour de la classe d'aide
Ici, j'ai ajouté le champ constructorInfoInvokeMethodInfo
pour la récupération de la MethodInfo
para el ConstructorInfo.Invoke()
(à appeler à partir de l'intérieur du 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[]) });
}
Mise à jour du test pour Expression.Block
Ici, j'ai remplacé le (non fonctionnel) Expression.New
avec un Expression.Call
pour instancier le 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);
}