【问题标题】:Covariance for classes with list members in C#C#中具有列表成员的类的协方差
【发布时间】:2016-07-28 07:40:35
【问题描述】:

考虑如下所示的类层次结构。本质上,有一个抽象的ComplexBase,其中包含所有类共有的一些字段。然后有一个派生自ComplexBaseComplexClass,其中包含同样派生自ComplexBaseComplexElements 的集合。

public abstract class ComplexBase {
  internal abstract string Identifier { get; }
}

public abstract class ComplexClass<T> : ComplexBase where T : ComplexElement {
  internal SortedList<string, T> Elements { get; set; }
}

public abstract class ComplexElement : ComplexBase { }

抽象类的实现是ComplexClassAComplexClassB。前者的ComplexElement 集合仅包含ComplexElementA 的实例,而后者仅包含ComplexElementB 的实例。

public class ComplexClassA : ComplexClass<ComplexElementA> {
  public ComplexClassA() {
    Elements = new SortedList<string, ComplexElementA>();
  }
}

public class ComplexElementA : ComplexElement { }

public class ComplexClassB : ComplexClass<ComplexElementB> {
  public ComplexClassB() {
    Elements = new SortedList<string, ComplexElementB>();
  }
}

public class ComplexElementB : ComplexElement { }

我很难理解的是如何定义一个新类TheBigCollection 包含各种字段和方法,以及ComplexClassAComplexClassB 的实例集合。非工作版本可能看起来像

public class TheBigCollection {
  internal SortedList<string, ComplexClass> Classes { get; set; }

  public TheBigCollection() {
    Classes = new SortedList<string, ComplexClass>();
  }

  public void Add(string name, ComplexClass element) {
    Classes.Add(name, element);
  }
}

当然这不会编译,因为ComplexClass 是用泛型类型定义的。

我的previous attempt 是使用隐藏列表的概念,但事实证明,这会阻止我在我从列表中检索到的ComplexClassAComplexClassB 的实例中访问ComplexElements 的列表TheBigCollection.

我从之前的帖子中了解到协方差可以解决问题,但我不明白如何使用 IEnumerable(它是不可变的)来向 TheBigCollection 的类列表添加新元素。

【问题讨论】:

  • 您是否考虑过创建一个中间类 ComplexClassBase : ComplexBase 并从 ComplexClassBase 继承 ComplexClass
  • @michauzo 是的,我做到了,非常符合this post 的答案。此解决方案出现的问题是,如果没有适当的演员表,我将无法遍历列表 Classes 并访问每个项目的 Elements.Count 属性。
  • 在中间类(或接口)中,您可以将属性 SortedList Elements { get;放; } 然后使用专门的类型在 ComplexClass 中实现它。
  • 不幸的是,这意味着对于ComplexClass 的任何实现,中间类中的Elements 将是null。专用类型将隐藏更通用的属性SortedList&lt;string, ComplexElement&gt;,并且TheBigCollectionClasses 列表中的任何项目将显示Elementsnull

标签: c# list generics covariance


【解决方案1】:

我尝试了以下方法,效果很好。

internal interface IComplexClass
{
    IDictionary<string, ComplexElement> Elements { get; }
}

public abstract class ComplexClass<T> : ComplexBase, IComplexClass where T : ComplexElement
{
    internal SortedList<string, T> Elements { get; set; }

    IDictionary<string, ComplexElement> IComplexClass.Elements
    {
        get { return (IDictionary<string, ComplexElement>)Elements; }
    }
}

现在您可以在 TheBigCollection 中使用 IComplexClass。

【讨论】:

  • 这看起来不错——我不知道可以通过使用类名引用基类的 getter 来覆盖它——但是这里也有一个问题。想象一下,我对TheBigColletcion 实例的成员Classes 有一个Linq 查询。在运行时访问查询结果项目的Elements 时,getter 会抛出System.InvalidCastException,因为它尝试将带有ComplexClassAs 的SortedList 转换为带有ComplexClasses 的IDictionary .
  • 一种确实按计划提供访问权限并避免System.InvalidCastException 的getter 是通过使用get { return Elements.ToDictionary(el =&gt; el.Key, el =&gt; (ComplexElement)el.Value); } 将演员表放入KeyValuePair 的方法。令人惊讶。
  • 确实如此。您的解决方案不是很优雅,但它很有效,到目前为止我还没有看到更好的解决方案。
猜你喜欢
  • 2013-06-18
  • 2021-04-09
  • 1970-01-01
  • 2018-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多