12
votes

Obtenir le type de retour réel d'une expression > instance

J'ai une méthode qui accepte une expression > instance. Je veux que le type de données réelle soit renvoyé par une instance d'expression spécifique plutôt que par objet .

Je peux le faire fonctionner pour des références de propriété directe, donc si je passe dans l'expression x => x.integerproperty je peux obtenir une référence de type pour un entier. Cette approche nécessite de la convertir en une mémorexion.

Cependant, je ne peux pas le faire travailler pour des expressions arbitraires. Par exemple, si l'expression est x => x.integerproperty.tostring () je veux obtenir une référence de type pour une chaîne. Je ne peux pas compiler cela à une mémorexion, et si je viens de .Compile () et vérifier le type de retour que j'obtiens "objet".

Comment puis-je regarder l'instance d'expression spécifique et dériver le type de retour réel?


1 commentaires

Techniquement, le type de retour réel de l'expression est ... objet . Étant donné que la fonction était nécessaire pour renvoyer objet , les expressions nécessaires ont été générées pour s'assurer que le type est renvoyé (une conversion dans ce cas).


3 Réponses :


3
votes

Bien que pas impossible, c'est particulièrement difficile. Il faudrait marcher dans l'arbre d'expression et faire une logique potentiellement complexe. Par exemple, que voudriez-vous voir si je suis passé dans l'expression suivante? XXX

Cette méthode pourrait renvoyer un int ou a chaîne .

MAINTENANT, vous pourrez peut-être faire plus d'avance en déchargeant une partie de cette logique sur le compilateur. Vous pouvez modifier votre paramètre de méthode à partir de Func à Func et utilisez typeof (Treturn) dans la méthode Pour déterminer ce que le compilateur a décidé le type de retour de l'expression était.

Bien sûr, dans le cas de mon exemple, vous travaillerez toujours contre objet . Mais, votre exemple de x => x.integerproperty.tostring () donnera string , ce que vous recherchez.


3 commentaires

Vous faites de bons points, mais cela est utilisé dans certaines circonstances spécifiques où ces cas de bord ne devraient pas arriver (ou peuvent être facilement évités en les connaissant). Merci!


En fait, @adam Maras, votre exemple ne compilerait même jamais car l'opérateur ternaire lancerait une erreur d'heure de compilation disant "type d'expression conditionnelle ne peut pas être déterminé car il n'y a pas de conversion implicite entre" int 'et "chaîne" "


C'était plus d'exercice de pensée qu'un échantillon de code littéral.



-1
votes

Un peu d'une manière effrontée (et cela implique d'appeler réellement le Func code>), mais vous pouvez le faire:

using System;

class Program
{
    static Func<T,object> MakeFunc<T>()
    {
        return x => 23;
    }

    static Type GetReturnType<T>(Func<T,object> f)
    {
        return f(default(T)).GetType();
    }

    static void Main(string[] args)
    {
        Type t = GetReturnType(MakeFunc<string>());
        Console.WriteLine(t);
    }
}


2 commentaires

Je ne sais pas comment je me sens à propos de l'exécution de l'expression pour obtenir le type de retour; Et s'il y a des effets secondaires sur la fonction? Dans mon cas particulier qui n'est pas une grosse inquiétude, mais j'ai accepté une réponse qui semble fonctionner aussi bien et n'exige pas d'exécution. Merci quand même!


@Seth: Personnellement, je ne serais pas trop content de l'exécution de l'expression (c'est une limitation de l'approche) - c'était juste le meilleur que je puisse venir sur le dessus de ma tête. L'un des avantages de cette approche est que cela fonctionne sur FUNC des instances plutôt que sur Expression .



24
votes

quelque chose comme ça pourrait faire le tour. Cela ne couvre probablement pas toutes les possibilités, mais c'est un début.

public static Type GetObjectType<T>(Expression<Func<T, object>> expr)
{
    if ((expr.Body.NodeType == ExpressionType.Convert) ||
        (expr.Body.NodeType == ExpressionType.ConvertChecked))
    {
        var unary = expr.Body as UnaryExpression;
        if (unary != null)
            return unary.Operand.Type;
    }
    return expr.Body.Type;
}


4 commentaires

Je ne pouvais penser à aucun autre type d'expression que cela pourrait éventuellement être si ce n'était pas réellement objet . Couvre tous les cas que je pouvais penser.


Travaillé grand. J'utilise cela dans un ensemble de circonstances assez spécifiques, donc si là sont des cas de bord que vous n'avez pas couvert ils ne me font pas encore de douleur. Merci!


N'est pas assez expr ..type assez? Je me manque mal lorsque vous utilisez le code ci-dessus avec, par exemple, i => (flotteur) i où je suis un entier. Ensuite, ce code retourne entier et ne pas flotter. Mais simplement expr.body.type fonctionne.


@Andreas: Êtes-vous sûr? Mon code ci-dessus renvoie correctement float lorsque je passe dans (int i) => (float) i , alors que vous utilisez juste expr.body.type renvoie objet .