[英]Select the type of a covariant interface in a class that implement multiple covariant interfaces
我们有一个带有协变参数定义的简单数据提供者接口。
interface IDataProvider<out T>
{
T Get();
}
打印机 class 将打印所有 IMessage 的值。
class Printer
{
private readonly IEnumerable<IDataProvider<IMessage>> DataProviders;
public Printer(params IDataProvider<IMessage>[] dataProviders)
{
DataProviders = dataProviders;
}
public void Print()
{
foreach( var dataProvider in DataProviders)
{
Console.WriteLine( dataProvider.Get().Message );
}
}
}
interface IMessage
{
string Message { get; }
}
如果所有 class 都实现一个 IDataProvider,则行为如我所料。 以下代码将打印“Hello”和“World”。
class Program
{
static void Main(string[] args)
{
HelloProvider helloProvider = new HelloProvider();
WorldProvider worldProvider = new WorldProvider();
Printer printer = new Printer(helloProvider, worldProvider);
printer.Print();
}
}
class Hello : IMessage
{
public string Message { get; } = "Hello";
}
class World : IMessage
{
public string Message { get; } = "World";
}
class HelloProvider : IDataProvider<Hello>
{
public Hello Get() => new Hello();
}
class WorldProvider : IDataProvider<World>
{
public World Get() => new World();
}
但是我们有一个坏男孩实现了两个 IDataProvider:
class BadBoy : IDataProvider<Hello>, IDataProvider<World>
{
// Assume that there are some shared state of Hello and World data.
Hello IDataProvider<Hello>.Get() => new Hello();
World IDataProvider<World>.Get() => new World();
}
我尝试投射 BadBoy,但打印机打印两个“Hello”。
BadBoy badBoy = new BadBoy();
Printer printer = new Printer( (IDataProvider<Hello>)badBoy, (IDataProvider<World>)badBoy );
printer.Print();
我们现在最好的解决方案是单独的 IDataProvider 实现:
class HelloProvider : IDataProvider<Hello>
{
private readonly BadBoy BadBoy;
public HelloProvider(BadBoy badBoy)
{
BadBoy = badBoy;
}
public Hello Get() => BadBoy.GetHello();
}
class WorldProvider : IDataProvider<World>
{
private readonly BadBoy BadBoy;
public WorldProvider(BadBoy badBoy)
{
BadBoy = badBoy;
}
public World Get() => BadBoy.GetWorld();
}
class BadBoy
{
// Assume that there are some shared state of Hello and World data.
public Hello GetHello() => new Hello();
public World GetWorld() => new World();
}
我对这个解决方案不满意,因为随着我们不断地向系统添加新的数据类型,它可能会导致大量的仪式代码。 有没有更好的方法来告诉打印机 class 它应该使用哪个 IDataProvider 实现?
笔记:
这个例子是我们实际问题的简化版本。 在我们的项目中,我们从容器中解析所有 IDataProvider,并使用反射来使用这些 IDataProvider 创建许多计算类。 如果您需要更多关于实际问题的详细信息,请告诉我。
你通过复制合同把自己画到了一个角落。 但是(并且不让您重新设计整个解决方案),一种方法(尽管很讨厌)是通过使用GetInterfaces
和GetInterfaceMap
然后调用您需要的方法来反映接口,如果需要,您可能会缓存 MethodInfo。
返回指定接口类型的接口映射。
注意:这可能对您有帮助,也可能对您没有帮助(取决于您的情况) ¯\_(ツ)_/¯
class Printer
{
public void Print()
{
foreach (var dataProvider in DataProviders)
{
var dataProviderType = dataProvider.GetType();
foreach (var type in dataProviderType.GetInterfaces())
{
var methodInfo = dataProviderType
.GetInterfaceMap(type)
.TargetMethods
.FirstOrDefault(x => x.Name.EndsWith("Get"));
var invoke = (IMessage) methodInfo.Invoke(dataProvider, null);
Console.WriteLine(invoke.Message);
}
}
}
...
用法
BadBoy badBoy = new BadBoy();
Printer printer = new Printer(badBoy);
printer.Print();
Output
Hello
World
更新
使用MethodInfo
缓存
public List<MethodInfo> _cache;
public void Print()
{
foreach (var dataProvider in DataProviders)
{
var dataProviderType = dataProvider.GetType();
if (_cache == null)
{
_cache = dataProviderType.GetInterfaces().Select(x => dataProviderType.GetInterfaceMap(x)
.TargetMethods
.FirstOrDefault(x => x.Name.EndsWith("Get"))).ToList();
}
foreach (var item in _cache)
{
var invoke = (IMessage) item.Invoke(dataProvider, null);
Console.WriteLine(invoke.Message);
}
}
}
在我的电脑上,我可以在 400 毫秒内调用原始的 100,000 次,使用缓存,我可以在 60 毫秒内调用它,100,000 次。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.