从Expression<Func<T, object>>实例中获取实际返回类型

12
我有一个方法接受一个Expression<Func<T, object>>实例。我想获取特定表达式实例返回的实际数据类型,而不是object。对于直接属性引用,我可以让它工作,所以如果我传递表达式x => x.IntegerProperty,我可以获得整数的Type引用。这种方法需要将其转换为MemberExpression。然而,我无法使任意表达式工作。例如,如果表达式是x => x.IntegerProperty.ToString(),我想要获取字符串的Type引用。我无法将其编译为MemberExpression,如果我只使用.Compile()并检查返回类型,我会得到“object”。我该如何查看特定表达式实例并推导出实际的返回类型?

从技术上讲,表达式的实际返回类型是 object。由于函数需要返回 object,所以生成了必要的表达式来确保它是返回的类型(在这种情况下进行转换)。 - Jeff Mercado
3个回答

24

这样做可能会有所帮助。它可能不包含每种可能性,但它是一个开始。

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

如果它实际上不是“object”,我想不出任何其他可能的表达类型。 它涵盖了我能想到的所有情况。 - Jeff Mercado
运行得非常好。我在相当特定的情况下使用它,所以如果你还没有涵盖到某些边缘情况,目前它们还没有给我带来任何痛苦。谢谢! - Seth Petry-Johnson
expr.Body.Type不够吗?当我使用上面的代码时,例如i => (float)i,其中i是整数时,我得到了错误的结果。然后这段代码返回整数而不是浮点数。但是简单的expr.Body.Type就可以工作。 - Andreas Zita
@Andreas:你确定吗?我的上面的代码在传入(int i) => (float)i时正确返回了float,而仅使用expr.Body.Type则返回object - LukeH

3

虽然不是不可能,但这特别困难。它需要遍历表达式树并执行一些可能复杂的逻辑。例如,如果我传入以下表达式,你希望看到什么?

Func<bool, object> expr = switch => switch ? 1 : "False";

这种方法可能会返回intstring

现在,你可以通过将一些逻辑交给编译器来更好地解决问题。你可以将方法参数从Func<T, object>改为Func<T, TReturn>,并在方法中使用typeof(TReturn)来确定表达式的返回类型由编译器决定。

当然,在我的例子中,你仍然会使用object。但是,你的例子x => x.IntegerProperty.ToString()将产生string,这正是你要找的。


你提出了一些好的观点,但这是在某些特定情况下使用的,那些边缘案例不应该出现(或者可以通过了解它们轻松避免)。谢谢! - Seth Petry-Johnson
5
@Adam Maras,实际上,你的例子甚至无法编译,因为三元运算符会抛出一个编译时错误,指出“条件表达式的类型无法确定,因为'int'和'string'之间没有隐式转换”。 - Michiel Cornille
这更多是一种思维练习,而不是字面上的代码示例。 - Adam Maras

-2

有一种有点无耻的方法(它实际上涉及调用Func),但你可以这样做:

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

不能保证在所有情况下都能正常工作,我应该补充说明 - 特别是如果 default(T) 不是 Func 的有效参数。但至少它是一个潜在的起点。


4
我不确定执行获取返回类型的表达式会让我感觉如何;如果该函数有副作用,那会怎样呢?在我的具体情况下,这不是一个大问题,但我接受了一个看起来同样有效且不需要执行的答案。谢谢! - Seth Petry-Johnson
1
@Seth:就个人而言,我对执行这个表达式并不太满意(这是一种方法的限制)- 这只是我能够脑海中想到的最好的方法。这种方法的一个优点是它适用于Func实例而不是Expression实例。 - Stuart Golodetz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接