繁体   English   中英

Select 实现多个协变接口的 class 中的协变接口类型

[英]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 创建许多计算类。 如果您需要更多关于实际问题的详细信息,请告诉我。

你通过复制合同把自己画到了一个角落。 但是(并且不让您重新设计整个解决方案),一种方法(尽管很讨厌)是通过使用GetInterfacesGetInterfaceMap然后调用您需要的方法来反映接口,如果需要,您可能会缓存 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM