2017-01-06 5 views
0

Предисловие: Я знаю, что есть много вопросов и ответов о ковариации и контравариантности, но я все еще чувствую себя путаным и не знаю, какое решение реализовать.Ковариантность путаницы. Невозможно назначить кортежи реализованных интерфейсов в список кортежей

У меня есть два интерфейса, реализация которых предназначена для совместного использования в парах. В одной из них содержится информация о позиции продаж, а на одной из них предоставляется информация, зависящая от языка, для позиции продаж.

Я не имею контроля над этими интерфейсами:

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); 

ответ

1

Используйте модификатор out для параметра типа общего типа, чтобы получить covariance в этом параметре.

В текущей версии C#, это не поддерживается для class типов, только interface типов (и типов делегатов), так что вам нужно будет написать интерфейс (уведомление использование в out):

public interface IReadableItemAndTranslationPair<out TItem, out TItemTranslation> 
    where TItem : class, IItem 
    where TItemTranslation : class, IItemTranslation 
{ 
    TItem Item { get; } 
    TItemTranslation Translation { get; } 
} 

Обратите внимание, что свойства не могут иметь аксессуар set, поскольку это несовместимо с ковариацией.

С этого типа, вы можете иметь:

var differentBrandsOfBeans = SomeService.GetCansOfBeans(); 
//above variable is of type 
//IEnumerable<IReadableItemAndTranslationPair<GoodsItem, GoodsTranslation>> 

var items = new List<IReadableItemAndTranslationPair<IItem, IItemTranslation>>(); 

items.AddRange(differentBrandsOfBeans); 

Это работает, потому что IEnumerable<out T> ковариантен, и ваш тип IReadableItemAndTranslationPair<out TItem, out TItemTranslation> ковариантен в обоих TItem и TItemTranslation.

+0

Большое спасибо за ваш ответ. Но как я могу создать экземпляр одного из этих пар? например внутри метода 'GetCansOfBeans()', какую функцию я использую «IReadableItemAndTranslationPair»? Здесь я теряюсь в других онлайн-QAs для ковариации и модификатора 'out'. – Zac

+0

@Zac У вас должен быть класс, например 'class ItemAndTranslationPair ', который реализует интерфейс 'IReadableItemAndTranslationPair '. Класс может иметь как 'get', так и' set' для своих свойств (только часть 'get' будет частью контракта« интерфейса »). Возвращаемый тип метода GetCansOfBeans() должен включать только интерфейс, но фактические пары, которые вы «уступаете», должны быть экземплярами класса. –

+0

Cheers @ Jeppe. Наличие как части 'get', так и' set' на реализации, в то время как только «get» на контракте был там, где я путался. Я могу жить с методом 'GetCansOfBeans()', возвращающим интерфейс, хотя мои другие методы, такие как 'DeleteCansOfBeans (IEnumerable >)', по-видимому, больше не будут работать с этими списками, что кажется неудовлетворительным. – Zac

1

Вам нужно создать кортежи, используя типы интерфейсов:

var canOfBeans = new Tuple<IItem, IItemTranslation>(new GoodsItem(), new GoodsTranslation()); 
var massage = new Tuple<IItem, IItemTranslation>(new ServiceItem(), new ServiceTranslation()); 

Как вы храните их в списке Tuple<IItem, IItemTranslation> этого должно быть проблемой для вас.

+0

Спасибо за ответ @Sean. Я добавил предостережение в свой ответ. Но по существу сервисный уровень возвращает строго типизированные пары. Я также обновил ответ, чтобы показать, как я действительно пытаюсь представить эти пары, это на самом деле не Tuple, а класс с типичными параметрами типа. Я пытаюсь избежать обхода обратно в интерфейс при добавлении этих элементов в список. – Zac

+1

@Zac Вы не можете избежать ввода их в качестве интерфейса, а не основного типа, потому что это то, что требует «Список». – Servy

 Смежные вопросы

  • Нет связанных вопросов^_^