我有以下类型层次结构:
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();
});
}
我需要在T
中找出Func<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));
}
答案 0 :(得分:8)
每个方法都有一个存储在程序集中的返回类型,您指定方法的返回类型为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);
答案 1 :(得分:6)
C#中的Lambdas根据分配给它们的表达式获取它们的类型。例如,这个:
Func<string, string> d = x => x;
使lambda x => x
成为string => string
函数,理由是这是变量d
所期望的。
为什么这有关系?因为在以下代码中,您正在为以下lambdas创建类似的期望:
var factories = new Func<Controller>[] {
() => new PersonController(),
() => new OrderController()
};
期望这些lambdas属于Func<Controller>
类型。如果编译器正在查看类型的lambda实体,它将得出一个不同的结论,特别是一个是Func<PersonController>
而另一个是Func<OrderController>
,两者都是{{ 1}}。但是编译器没有查看类型的主体,它查看变量,在这种情况下“变量”是数组槽。
因此,编译器将生成这些委托而不是这些方法:
Func<Controller>
但是正如这些方法:
PersonController Delegate1()
{
return new PersonController();
}
OrderController Delegate2()
{
return new OrderController();
}
因此,当这些委托插入到数组中时,只保留签名的类型信息,而lambda实体的实际类型信息将丢失。
但是有一种方法可以维护有关lambda主体的信息,然后检查该信息而不是委托签名。您可以使用表达式树,这是告诉编译器将代码视为数据并基本上生成表示 lambda的树对象的方式,而不是实际的方法实现 lambda。
他们的语法几乎与lambdas的语法相同:
Controller Delegate1()
{
return new PersonController();
}
Controller Delegate2()
{
return new OrderController();
}
不同之处在于,现在这些对象不是函数,而是函数的表示。您可以遍历这些表示,并且很容易找到其顶级表达式的类型:
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)
答案 2 :(得分:3)
问题是相同的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
。
答案 3 :(得分:2)
这是它的工作原理,当你实例化你的数组时,你指定它将存储类型为Func
的{{1}}个对象,现在你要实例化数组中的子类型对象Controller
,Resolve,不知道实际类型是什么,除非您调用Func
来确定它的实际类型,它只知道GetType()
将是{{1}类型因为约束,即T