如何使用反射获取Func <T>的返回类型?

MaY*_*YaN 8 .net c# reflection

我有以下类型层次结构:

public abstract class Controller {}
public sealed class PersonController : Controller {}
public sealed class OrderController : Controller {}
Run Code Online (Sandbox Code Playgroud)

我还有一个方法可以根据需要解析给定类型的实例(将其视为外行人的IOC):

private void Resolve<T>(Func<T>[] controllerFactories) where T : Controller
{
    Array.ForEach(controllerFactories, x => 
    {
        // This returns Controller instead of the child class
        // I need to know the actual type so that I can do other stuff
        // before resolving the instance.
        Console.WriteLine(x.Method.ReturnType);
        var controllerInstance = x();
    });
}
Run Code Online (Sandbox Code Playgroud)

我需要弄清楚Tin 的类型Func<T>但是当我尝试时:

void Main()
{
    var factories = new Func<Controller>[] {
        () => new PersonController(),
        () => new OrderController()
    };

    Resolve(factories);
}
Run Code Online (Sandbox Code Playgroud)

我得到Controller而不是PersonControllerOrderController.

有任何想法吗?

更新:

我感谢大家提供了详细的答案和例子.他们促使我重新思考API,这就是我最终想出的东西,它是我想要完成的:
public interface IResolver<T>
{
    void Register<TKey>(Func<TKey> factory) where TKey : T;
    T Resolve(Type type);
    void ResolveAll();
}

public sealed class ControllerResolver : IResolver<Controller>
{
    private Dictionary<Type, Func<Controller>> _factories = new Dictionary<Type, Func<Controller>>();

    public void Register<TKey>(Func<TKey> factory) where TKey : Controller
    {
        _factories.Add(typeof(TKey), factory);
    }

    public Controller Resolve(Type type) => _factories[type]();

    public void ResolveAll()
    {
        foreach (var pair in _factories)
        {
            // Correctly outputs what I want
            Console.WriteLine(pair.Value.Method.ReturnType);            
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是一些使用示例:

void Main()
{
    var resolver = new ControllerResolver();
    resolver.Register(() => new PersonController());
    resolver.Register(() => new OrderController());   

    resolver.ResolveAll();
    resolver.Resolve(typeof(PersonController));
}
Run Code Online (Sandbox Code Playgroud)

Zei*_*kki 8

每个方法都有一个存储在程序集中的返回类型,您指定了方法的返回类型Controller.这是唯一可以保证作为信息的东西(这是我们所知道的,不执行该方法 - 也是在编译时)

所以我们知道该方法应该返回Controller或者从中得到的任何东西.在我们实际调用该方法之前,我们永远不知道什么是运行时类型.

例如,如果该方法具有如下代码,该怎么办?

var factories = new Func<Controller>[] {
    () =>
    {
        if ( DateTime.Now.Second % 2 == 0 )
            return new OrderController();
        else
            return new PersonController();
    }
};
Run Code Online (Sandbox Code Playgroud)

如果需要获取返回的对象的运行时类型,则需要:

var controllerInstance = x();
Console.WriteLine(controllerInstance.GetType().Name);
Run Code Online (Sandbox Code Playgroud)

  • @MaYaN这是你不可能做到的.除非你运行它,否则你永远无法知道该方法会返回什么!想想具有多个if语句的方法,检查当前秒是偶数还是奇数,然后基于此返回不同的对象. (3认同)

The*_*kis 6

C#中的Lambdas根据它们所分配的表达式获取它们的类型.例如,这个:

Func<string, string> d = x => x;
Run Code Online (Sandbox Code Playgroud)

使lambda x => x成为一个string => string函数,理由是这是变量所d期望的.

为什么这有关系?因为在以下代码中,您为以下lambda创建了类似的期望:

var factories = new Func<Controller>[] {
    () => new PersonController(),
    () => new OrderController()
};
Run Code Online (Sandbox Code Playgroud)

期望这些lambda属于这种Func<Controller>类型.如果编译器正在查看类型的lambda实体,那么它将得出一个不同的结论,特别是一个是a Func<PersonController>而另一个是a Func<OrderController>,两者都是Func<Controller>.但编译器不查看类型的主体,它查看变量,在这种情况下"变量"是数组槽.

因此,编译器将生成这些委托而不是这些方法:

PersonController Delegate1()
{
    return new PersonController();
}

OrderController Delegate2()
{
    return new OrderController();
}
Run Code Online (Sandbox Code Playgroud)

但是作为这些方法:

Controller Delegate1()
{
    return new PersonController();
}

Controller Delegate2()
{
    return new OrderController();
}
Run Code Online (Sandbox Code Playgroud)

因此,在将这些委托插入到数组中时,仅保留签名的类型信息,而丢失了λ体的实际类型信息.

但是有一种方法可以维护有关lambda主体的信息,然后检查该信息而不是委托签名.您可以使用表达式树,这是一种告诉编译器将代码视为数据并基本上生成表示 lambda的树对象的方法,而不是实现 lambda 的实际方法.

它们的语法几乎与lambdas的语法相同:

var factories = new Expression<Func<Controller>>[] {
    () => new PersonController(),
    () => new OrderController()
};
Run Code Online (Sandbox Code Playgroud)

不同之处在于,现在这些对象不是函数,而是函数的表示.您可以遍历这些表示,并且很容易找到它们的顶级表达式的类型:

var t0 = factories[0].Body.Type; // this is equal to typeof(PersonController)
var t1 = factories[1].Body.Type; // this is equal to typeof(OrderController)
Run Code Online (Sandbox Code Playgroud)

最后,如果您还打算运行它们,也可以将这些表示转换为实际函数:

Func<Controller>[] implementations = factories.Select(x => x.Compile()).ToArray();
Run Code Online (Sandbox Code Playgroud)