У нас есть простой интерфейс поставщика данных с ковариантным определением параметров.
interface IDataProvider<out T>
{
T Get();
}
Класс принтера будет печатать значение всех сообщений 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; }
}
Если все классы реализуют один 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, но принтер напечатал два » Привет ".
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();
}
Я не удовлетворен этим решением, потому что оно может привести к множеству церемониальных кодов поскольку мы постоянно добавляем новый тип данных в нашу систему. Есть ли лучший способ сообщить классу Printer, какую реализацию IDataProvider он должен использовать?
примечание:
Этот пример представляет собой упрощенную версию нашей реальной проблемы. В нашем проекте мы разрешаем все IDataProvider из контейнера и используем отражение для создания множества классов вычислений с использованием этих IDataProvider. Скажите, пожалуйста, если вам нужна более подробная информация о реальной проблеме.