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



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

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

我无法控制这些接口

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类或它们的派生类型的情况下,传递这些配对的泛型列表而不在接口和它们的类型之间来回转换的最干净的方法是什么?

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

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

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

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

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

对泛型类型的类型参数使用out修饰符,以获得该参数中的协方差

在当前版本的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>TItemTItemTranslation中都是协变。

最新更新