2 votes

Existe-t-il un moyen d'instancier une classe via Expression.New où 'ConstructorInfo' est fourni par un ExpressionParameter ?

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X