[英]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
而不是PersonController
和OrderController
。
有任何想法嗎?
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
嗎? 為了使其適合您的用例,它只能是PersonController
和OrderController
的超類,而必須是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.