协方差混淆。无法将已实现接口的元组分配给元组列表

时间:2017-01-06 11:57:52

标签: c# generics covariance contravariance invariance

前言:我知道有很多关于协方差和逆变的问题和答案,但我仍感到困惑,不确定要实施什么解决方案。

我有两个接口,其实现旨在成对使用。一个提供有关销售项目的信息,另一个提供销售项目的语言相关信息。

我无法控制这些界面

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>'    }
}

问题:在不更改我的IItemITranslation类或其衍生类型的情况下,能够传递通用列表的最简洁方法是什么?这些配对没有在界面和它们的类型之间来回摆动?​​

买者。我试图简化问题,但我实际上并没有使用元组。实际上,我使用的是这样的类:

public class ItemAndTranslationPair<TItem, TItemTranslation> where TItem : class, IItem where TItemTranslation : class, IItemTranslation
{
    TItem Item;
    TTranslation Translation;
}

我的服务正在返回强类型列表,例如List<ItemAndTranslationPair<GoodsItem, GoodsTranslation>>,因此当我将项目添加到&#39; generic&#39;列出它看起来像:

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

var items = new List<ItemAndTranslationPair<IItem, IItemTranslation>>();
items.AddRange(differentBrandsOfBeans);

2 个答案:

答案 0 :(得分:2)

您需要使用接口类型创建元组:

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

当您将它们存储在Tuple<IItem, IItemTranslation>列表中时,这对您来说应该是一个问题。

答案 1 :(得分:1)

在泛型类型的type参数上使用out修饰符,以获取该参数中的协方差

在当前版本的C#中,class类型,仅interface类型(和委托类型)不支持此类,因此您需要编写一个接口(注意使用{{1 }}):

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中都是协变的。