【问题标题】:Get "Object" return type from Func<MyClass, object>从 Func<MyClass, object> 获取“对象”返回类型
【发布时间】:2013-07-24 14:29:27
【问题描述】:

假设我已将 Func 定义如下:

Func<MyClass, object> f = o => o.StringProperty;

Func<MyClass, object> f = o => o.Property.SomeMethod();

有没有办法在不专门调用的情况下获得 实际 返回类型?

【问题讨论】:

  • “获取”返回类型究竟如何?一个假设的例子?
  • @Jon 我认为根据编译器知道这一点的事实推断返回类型(第一个示例中为string,第二个示例中返回SomeMethod),但是此示例在运行时会崩溃-time 作为变量f 可以重新分配。假设,如果无法重新分配 f,您可以通过解析 Func 定义来静态确定返回类型。听起来像是罗斯林的候选人;-)
  • 根据您的使用情况,也许您可​​以利用Expression API 来检查返回类型:stackoverflow.com/questions/671968/…(这不是您想要做的确切实现,只是一个相关示例)
  • 如果你有类似Func&lt;MyClass, object&gt; f1 = o =&gt; o.StringProperty; Func&lt;MyClass, object&gt; f2 = o =&gt; f1(o); 的东西,“实际”返回类型是什么?甚至Func&lt;MyClass, object&gt; f = o =&gt; { throw new NotImplementedException(); };? :)
  • @hvd 在抛出异常的情况下这是一个很好的问题

标签: c# reflection lambda expression


【解决方案1】:

你可以像这样得到返回类型:

f.Method.ReturnType

但这会返回类型object。如果您想获取MethodString 或派生自object 的内容,除非您调用该方法,否则您将无法获得信息。

实际上可以,但这意味着您必须分解方法核心,然后对其进行分析以查看该方法可以返回什么。但即便如此,也可能有许多不同的返回类型。

所以答案是:如果你想知道它返回一个对象,那么你可以,否则不值得麻烦,最好找到另一种方法来做你需要的事情。

【讨论】:

    【解决方案2】:

    由于您在运行时从其他来源检索这些 Func&lt;MyClass, object&gt; 委托,因此类型信息基本上丢失了。

    相反,在定义这些函数的地方,您可以让调用者在包装委托中编码该类型信息通过利用LINQ Expression API(编辑:愚蠢的我,更简单此时;我们已经有了通用的编译时间信息):

    public class MyClassDelegate
    {
        private readonly Func<MyClass, object> Function;
    
        public Type ReturnType { get; private set; }
    
        private MyClassDelegate(Func<MyClass, object> function, Type returnType)
        {
            this.Function = function;
            this.ReturnType = returnType;
        }
    
        public object Invoke(MyClass context)
        {
            return Function(context);
        }
    
        public static MyClassDelegate Create<TReturnType>(Func<MyClass, TReturnType> function)
        {
            Func<MyClass, object> nonTypedFunction = o => function(o);
            return new MyClassDelegate(nonTypedFunction, typeof(TReturnType));
        }
    }
    

    (也可以创建一个派生的泛型MyClassDelegate&lt;TReturnType&gt; : MyClassDelegate 类来解决Create 方法中的一些愚蠢问题,或者避免值类型装箱,或者在编译时提供返回类型信息,甚至通过反思MyClassDelegate&lt;TReturnType&gt; 是什么。)

    定义代理而不是直接使用Func&lt;MyClass, object&gt; 的调用者将使用此类并将其代理定义为:

    MyClassDelegate f1 = MyClassDelegate.Create(o => o.StringProperty);
    MyClassDelegate f2 = MyClassDelegate.Create(o => o.Property.SomeMethod());
    

    您的 API 需要 MyClassDelegate,您可以使用它轻松访问它们的类型:

    Console.WriteLine(f1.ReturnType.FullName); //string
    Console.WriteLine(f2.ReturnType.FullName); //whatever `SomeMethod()` is declared to return
    

    最后,您仍然可以调用委托甚至创建Func&lt;MyClass, object&gt; 委托:

    f1.Invoke(myClassInstance);
    Func<MyClass, object> f3 = f1.Invoke;
    

    【讨论】:

    • @EugeneD.Gubenkov:我已经编辑了我的答案;此时不需要 LINQ 表达式 API。关键是让调用者在创建委托时对这些信息进行编码,并利用包装好的委托在整个 API 中传递信息。
    • 感谢您的详细解答。当我们想要正确地做这些事情时,无论如何我们都应该使用表达式,我明白了。
    • Expression 的用法只有在您想避免泛型推理时才有帮助。也就是说,您可以拥有Create(Expression&lt;Func&lt;MyClass, object&gt;&gt; function) 并仍然确定使用的类型。但您不妨利用我在更新中使用的泛型类型推断。
    • 是的,我只是想找到非编译时解决方案,没有泛型类型。
    【解决方案3】:

    您可以通过使用泛型方法使编译器推断类型参数来做一些接近此的事情:

    static Func<T1, R> Infer<T1, R>(Func<T1, R> f) { return f; }
    

    然后:

    var func = Infer((string s) => s.Length);
    

    这将在编译时将返回类型编码为func 的类型。

    当然,对于更普遍适用的解决方案,您需要一堆 Infer 的重载,以使用一个、两个、三个等参数覆盖 ActionFunc

    如果您想在运行时获取返回类型,对于任何类型的 Func,它就像 ppetrov 已经指出的 func.Method.ReturnType 一样简单。

    【讨论】:

    • @EugeneD.Gubenkov:当然,如果你已经拥有Func&lt;string, object&gt; f = s =&gt; s.Length,而你使用Infer(f),你会得到一个Func&lt;string, object&gt;——没有办法恢复已经存在的类型信息“丢失”。
    • @EugeneD.Gubenkov:如果你想做更多,那么你必须使用Expression&lt;Func&lt;...&gt;&gt; 和反射来代替。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-13
    • 1970-01-01
    • 2012-03-15
    相关资源
    最近更新 更多