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而不是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);
}
}
}
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)
每个方法都有一个存储在程序集中的返回类型,您指定了方法的返回类型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)
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)