簡體   English   中英

如何獲得Func的返回類型 <T> 使用反射?

[英]How can I get the return type of a Func<T> using reflection?

我有以下類型層次結構:

public abstract class Controller {}
public sealed class PersonController : Controller {}
public sealed class OrderController : Controller {}

我也有一種方法可以按需解析給定類型的實例(將其視為外行的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();
    });
}

我需要弄清楚Func<T>T的類型,但是當我嘗試時:

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

    Resolve(factories);
}

我得到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); } } } 

這里是一些用法示例:

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

每個方法都有一個返回類型存儲在程序集中,您將方法的返回類型指定為Controller 這是唯一可以保證作為信息的東西( 這是我們在不執行方法的情況下所知道的-也在編譯時

因此,我們知道該方法應返回Controller或從中派生的任何東西。 在實際調用該方法之前,我們永遠無法知道什么是運行時類型。

例如,如果該方法具有如下代碼:

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

如果需要獲取返回的對象的運行時類型,則需要:

var controllerInstance = x();
Console.WriteLine(controllerInstance.GetType().Name);

C#中的Lambda會根據為其分配的表達式來獲取其類型。 例如,這:

Func<string, string> d = x => x;

使lambda x => x成為string => string函數,理由是這就是變量d期望的。

為什么這是相關的? 因為在以下代碼中,您正在為以下lambda創建類似的期望:

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

期望這些lambda為Func<Controller>類型。 如果編譯器正在查看該類型的lambda主體,則會得出不同的結論,特別是一個是Func<PersonController> ,另一個是Func<OrderController> ,這兩種都是Func<Controller> 但是編譯器不會查看類型的主體,而是查看變量,在這種情況下,“變量”是數組插槽。

因此,編譯器將不使用以下方法生成這些委托:

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

OrderController Delegate2()
{
    return new OrderController();
}

但是作為這些方法:

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

Controller Delegate2()
{
    return new OrderController();
}

因此,到將這些委托插入數組時,僅保留簽名的類型信息,而丟失了lambda主體的實際類型信息。

但是,有一種方法可以維護有關lambda主體的信息,然后檢查該信息而不是委托人簽名。 您可以使用表達式樹 ,這是一種告訴編譯器將代碼視為數據並本質上生成代表 lambda的樹對象的方法,而不是實現 lambda的實際方法。

它們的語法與lambda幾乎相同:

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

區別在於現在這些對象不是函數,而是函數的表示形式。 您可以遍歷這些表示形式,並很容易找到其頂級表達式的類型:

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

最后,如果您還打算運行它們,還可以將這些表示形式轉換為實際功能:

Func<Controller>[] implementations = factories.Select(x => x.Compile()).ToArray();

問題在於,同一T必須應用於數組中的每種類型。

private void Resolve<T>(Func<T>[] controllerFactories) where T : Controller
{
    Array.ForEach(controllerFactories, x => 
    {
        // This returns Controller instead of the child class
        Console.WriteLine(x.Method.ReturnType);
        var controllerInstance = x();
    });
}

你的T多少? PersonController嗎? OrderController嗎? 為了使其適合您的用例,它只能是PersonControllerOrderController的超類,而必須是Controller的子類。 因此T只能是一個類: Controller (假設這兩個類沒有擴展Controller的公共基類)。 使此類通用是沒有意義的。 與此完全相同:

private void Resolve(Func<Controller>[] controllerFactories)
{
    Array.ForEach(controllerFactories, x => 
    {
        Console.WriteLine(x.Method.ReturnType);
        var controllerInstance = x();
    });
}

而不是Func<Controller>實例的數組,傳遞在您的返回類型上鍵入的字典可能更有意義:

var factories = new Dictionary<Type, Func<Controller>> {
    [typeof(PersonController)] = () => new PersonController(),
    [typeof(OrderController)] = () => new OrderController()
};

Func<Controller>的實例只是不包含足夠的信息來自行確定其返回值的類型:並非不對其進行評估。 而且即使評估它,也要記住一個函數是一個黑匣子。 僅僅因為它一次返回一個PersonController並不意味着它在下次調用它時不會返回OrderController

這就是它的工作原理,當您實例化數組時,您指定它將存儲類型為Controller Func對象,現在您正在實例化Func數組內的子類型對象,即Resolve,不知道它是什么。實際類型,除非您調用GetType()來確定其實際類型,否則由於約束,它僅知道T將為Controller類型,即where T : Controller

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM