简体   繁体   English

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

[英]Covariance confusion. Can't assign tuples of implemented interfaces to list of tuples

Preface: I am aware that there are a lot of questions and answers about covariance and contravariance but I'm still feeling muddled and not sure what solution to implement. 前言:我知道关于协方差和协方差有很多疑问和答案,但我仍然感到困惑,不确定要实施哪种解决方案。

I have two interfaces whose implementations are intended to be used together in pairs. 我有两个接口,它们的实现旨在成对一起使用。 One provides the information about a sales item and one provides language dependent information for a sales item. 一个提供有关销售商品的信息,一个提供与语言有关的商品信息。

I do not have control over these interfaces : 我无法控制这些接口

public interface IItem
{
    decimal Price { get; set; }
}

public interface IItemTranslation
{
    string DisplayName { get; set; }
}

I also have two implementations of both these interfaces for a tangible GoodsItem as well as an intangible ServiceItem . 对于有形的GoodsItem和无形的ServiceItem我还具有这两种接口的两种实现。 Again, I do not have control over these interfaces : 同样, 我无法控制这些接口

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
}

As I said, these are classes that I do not have control over. 如我所说,这些是我无法控制的类。 I want to create a generic list of these pairings ( List<Tuple<IItem, IItemTranslation>> ) but I cannot: 我想创建这些配对的通用列表( 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>'    }
}

Question : Without changing my IItem and ITranslation classes or their derived types, what's the cleanest way to be able to pass around a generic list of these pairings without casting them back and forth between the interface and their type? 问题 :在不更改IItemITranslation类或它们的派生类型的情况下,能够通过这些配对的通用列表而不在接口及其类型之间来回转换的最干净的方法是什么?

Caveat. 警告。 I was trying to simplify the question but I'm not actually using Tuples. 我试图简化问题,但实际上并未使用Tuples。 In reality I'm using a class like this: 实际上,我使用的是这样的类:

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

and my services are returning strongly typed lists, like List<ItemAndTranslationPair<GoodsItem, GoodsTranslation>> and therefore when I add items to the 'generic' list it looks like: 并且我的服务正在返回强类型列表,例如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);

You need to create the tuples using the interface types: 您需要使用接口类型创建元组:

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

As you're storing them in a list of Tuple<IItem, IItemTranslation> this should be a problem for you. 当您将它们存储在Tuple<IItem, IItemTranslation>列表中时Tuple<IItem, IItemTranslation>这对您来说应该是一个问题。

Use the out modifier on the type parameter of the generic type to obtain covariance in that parameter. 在泛型类型的类型参数上使用out修饰符可获取该参数的协方差

In the current version of C#, this is not supported for class types, only interface types (and delegate types), so you would need to write an interface (notice use of 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; }
}

Note that the properties cannot have a set accessor since that would be incompatible with the covariance. 请注意,属性不能具有set访问器,因为这将与协方差不兼容。

With this type, you can have: 使用这种类型,您可以拥有:

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

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

items.AddRange(differentBrandsOfBeans);

It works because IEnumerable<out T> is covariant, and your type IReadableItemAndTranslationPair<out TItem, out TItemTranslation> is covariant in both TItem and TItemTranslation . 之所以有效,是因为IEnumerable<out T>是协变的,并且您的IReadableItemAndTranslationPair<out TItem, out TItemTranslation>类型在TItemTItemTranslation中都是协变的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM