【问题标题】:Get actual return type from a Expression<Func<T, object>> instance从 Expression<Func<T, object>> 实例获取实际返回类型
【发布时间】:2011-11-08 18:05:34
【问题描述】:

我有一个接受Expression&lt;Func&lt;T, object&gt;&gt; 实例的方法。我想获取特定表达式实例返回的 实际 数据类型,而不是 object

我可以让它为直接属性引用工作,所以如果我传入表达式x =&gt; x.IntegerProperty,我可以获得整数的类型引用。这种方法需要将其转换为 MemberExpression。

但是,我无法让它适用于任意表达式。例如,如果表达式是x =&gt; x.IntegerProperty.ToString(),我想获得一个字符串的类型引用。我无法将它编译为 MemberExpression,如果我只是 .Compile() 它并检查返回类型,我会得到“对象”。

如何查看具体的表达式实例并推导出实际的返回类型?

【问题讨论】:

  • 从技术上讲,表达式的实际返回类型是...object。由于该函数需要返回object,因此生成了必要的表达式以确保它是返回的类型(在本例中为转换)。

标签: c# reflection expression-trees


【解决方案1】:

这样的事情可能会奏效。它可能并未涵盖所有可能性,但这是一个开始。

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,我想不出任何其他可能的表达式类型。涵盖了我能想到的所有案例。
  • 效果很好。我在一组非常具体的情况下使用它,所以如果有个你没有涵盖的边缘情况,它们还没有给我带来任何痛苦。谢谢!
  • expr.Body.Type 还不够吗?将上面的代码与例如 i => (float)i 一起使用时,我得到错误的结果,其中 i 是一个整数。然后此代码返回整数而不是浮点数。但只是 expr.Body.Type 有效。
  • @Andreas:你确定吗?当我传入(int i) =&gt; (float)i 时,上面的代码正确返回float,而仅使用expr.Body.Type 会返回object
【解决方案2】:

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

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

此方法可以返回intstring

现在,您可以通过在编译器上卸载一些此类逻辑来取得更多进展。您可以将方法参数从Func&lt;T, object&gt; 更改为Func&lt;T, TReturn&gt;,并在方法中使用typeof(TReturn) 来确定编译器决定表达式的返回类型是什么。

当然,在我的示例中,您仍将针对object 工作。但是,您的 x =&gt; x.IntegerProperty.ToString() 示例将产生 string,这正是您要寻找的。​​p>

【讨论】:

  • 你提出了一些好的观点,但这被用于某些特定情况下,这些边缘情况不应该出现(或者通过了解它们可以很容易地避免)。谢谢!
  • 实际上,@Adam Maras,您的示例甚至永远不会编译,因为三元运算符会引发编译时错误,提示“无法确定条件表达式的类型,因为 'int' 之间没有隐式转换和“字符串”“
  • 这更像是一种思考练习,而不是文字代码示例。
【解决方案3】:

有点厚颜无耻(实际上涉及到调用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 的有效参数。但这至少是一个潜在的起点。

【讨论】:

  • 我不确定执行表达式以获取返回类型的感受;如果函数有副作用怎么办?在我的具体情况下,这不是一个大问题,但我接受了一个似乎同样有效且不需要执行的答案。不过谢谢!
  • @Seth:就我个人而言,我不会对执行表达式感到太高兴(这是方法的限制)——这只是我能想到的最好的方法。这种方法的一个优点是它适用于Func 实例而不是Expression 实例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多