Предисловие: Я знаю, что есть много вопросов и ответов о ковариации и контравариантности, но я все еще чувствую себя путаным и не знаю, какое решение реализовать.Ковариантность путаницы. Невозможно назначить кортежи реализованных интерфейсов в список кортежей
У меня есть два интерфейса, реализация которых предназначена для совместного использования в парах. В одной из них содержится информация о позиции продаж, а на одной из них предоставляется информация, зависящая от языка, для позиции продаж.
Я не имею контроля над этими интерфейсами:
public interface IItem
{
decimal Price { get; set; }
}
public interface IItemTranslation
{
string DisplayName { get; set; }
}
У меня также есть две реализации обоих этих интерфейсов для ощутимого GoodsItem
, а также нематериальных ServiceItem
. Опять же, я не имею контроля над этими интерфейсами:
public class GoodsItem : IItem
{
public decimal Price { get; set; } //implementation
public float ShippingWeightKilograms { get; set; } //Property specific to a GoodsItem
}
public class GoodsTranslation : IItemTranslation
{
public string DisplayName { get; set; } //implementation
public Uri ImageUri { get; set; } //Property specific to a GoodsTranslation
}
public class ServiceItem : IItem
{
public decimal Price { get; set; } //implementation
public int ServiceProviderId { get; set; } // Property specific to a ServiceItem
}
public class ServiceTranslation : IItemTranslation
{
public string DisplayName { get; set; } //implementation
public string ProviderDescription { get; set; } // Property specific to a ServiceTranslation
}
Как я уже сказал, это классы, которые не имеют контроля над. Я хочу, чтобы создать общий список этих пар (List<Tuple<IItem, IItemTranslation>>
), но я не могу:
public class StockDisplayList
{
public List<Tuple<IItem, IItemTranslation>> Items { get; set; }
public void AddSomeStockItems()
{
Items = new List<Tuple<IItem, IItemTranslation>>();
var canOfBeans = new Tuple<GoodsItem, GoodsTranslation>(new GoodsItem(), new GoodsTranslation());
var massage = new Tuple<ServiceItem, ServiceTranslation>(new ServiceItem(), new ServiceTranslation());
Items.Add(canOfBeans); //illegal: cannot convert from 'Tuple<GoodsItem, GoodsTranslation>' to 'Tuple<IItem, IItemTranslation>'
Items.Add(massage); //illegal: cannot convert from 'Tuple<ServiceItem, ServiceTranslation>' to 'Tuple<IItem, IItemTranslation>' }
}
Вопрос: Не меняя своих IItem
и ITranslation
классов или их производные типы, что чистый способ, чтобы иметь возможность обойти общий список этих спариваний, не отбрасывая их между интерфейсом и их типом?
Предостережение. Я пытался упростить вопрос, но на самом деле я не использую Tuples. На самом деле я использую класс, как это:
public class ItemAndTranslationPair<TItem, TItemTranslation> where TItem : class, IItem where TItemTranslation : class, IItemTranslation
{
TItem Item;
TTranslation Translation;
}
и мои услуги возвращаются сильно типизированных списки, как List<ItemAndTranslationPair<GoodsItem, GoodsTranslation>>
и поэтому, когда я добавить элементы в «общий» список выглядит так:
var differentBrandsOfBeans = SomeService.GetCansOfBeans();
//above variable is of type IEnumerable<ItemAndTranslationPair<GoodsItem, GoodsTranslation>>
var items = new List<ItemAndTranslationPair<IItem, IItemTranslation>>();
items.AddRange(differentBrandsOfBeans);
Большое спасибо за ваш ответ. Но как я могу создать экземпляр одного из этих пар? например внутри метода 'GetCansOfBeans()', какую функцию я использую «IReadableItemAndTranslationPair»? Здесь я теряюсь в других онлайн-QAs для ковариации и модификатора 'out'. – Zac
@Zac У вас должен быть класс, например 'class ItemAndTranslationPair', который реализует интерфейс 'IReadableItemAndTranslationPair '. Класс может иметь как 'get', так и' set' для своих свойств (только часть 'get' будет частью контракта« интерфейса »). Возвращаемый тип метода GetCansOfBeans() должен включать только интерфейс, но фактические пары, которые вы «уступаете», должны быть экземплярами класса. –
Cheers @ Jeppe. Наличие как части 'get', так и' set' на реализации, в то время как только «get» на контракте был там, где я путался. Я могу жить с методом 'GetCansOfBeans()', возвращающим интерфейс, хотя мои другие методы, такие как 'DeleteCansOfBeans (IEnumerable>)', по-видимому, больше не будут работать с этими списками, что кажется неудовлетворительным. –
Zac