1

假设我有这两个课程

class BaseClass 
{
    protected HashSet<BaseClass> container;
}


class DerivedClass : BaseClass
{
    DerivedClass() 
    {
        container = new HashSet<DerivedClass>(); 
    }
}                         

然后我收到一个错误:无法转换。

由于每个 DerivedClass(应该)都是 BaseClass,我不太确定为什么会抛出这个错误,但确实如此。

BaseClass 的目标是对 执行各种操作container,只有特别特定的行为与 DerivedClass 相关联——其中,要求容器类型为HashSet<DerivedClass>

这个目标通常如何实现?

4

4 回答 4

3

everyDevrivedClass是 a BaseClass,但反之则不然。HashSet<T>不能是协变的,因为它允许写操作 ( Add)。因此,在您的情况下,这是可能的:

class BaseClass 
{
   protected HashSet<BaseClass> container;

   public DoSomething()
   {
       container.Add(new BaseClass());   // not legal if container is really a List<DerivedClass>
   }
}

您可以将容器的类型更改为协变:

class BaseClass 
{
   protected IEnumerable<BaseClass> container;
}

class DerivedClass : BaseClass
{
   DerivedClass() 
  {
      container = new HashSet<DerivedClass>(); 
  }
}

但是只有派生类可以添加项目container(这可能在您的设计中起作用)。

您也可以尝试使基类泛型:

class BaseClass<T> where T:BaseClass<T> 
{
   protected HashSet<T> container;
}

class DerivedClass : BaseClass<DerivedClass>
{
   DerivedClass() 
  {
      container = new HashSet<DerivedClass>(); 
  }
}

这看起来有点奇怪,但是对于一个包含相同类型对象列表的类来说,这看起来很奇怪,所以在你的真实场景中它可能是有道理的。

于 2013-08-12T17:59:31.560 回答
2

你可以做的是:

class BaseClass<T> where T : BaseClass<T>
{
   protected HashSet<T> container;
}


class DerivedClass : BaseClass<DerivedClass>
{
   DerivedClass() 
  {
      container = new HashSet<DerivedClass>(); 
  }
}
于 2013-08-12T17:58:32.710 回答
0

仅接口支持协方差。即,container必须声明为ISet(它继承了 IEnumerable,它是协变的)。

编辑

正如 Nico 所说,这不会按预期工作。为了澄清为什么这是不可能的,请考虑:HashSet<BaseClass>.Add(DerivedClassA)是一个有效的操作,但HashSet<DerivedClassB>.Add(DerivedClassA)不是。因此,HashSet<DerivedClass> 不能HashSet<BaseClass>.

于 2013-08-12T18:01:27.093 回答
0

再扩展一点:基类有许多派生类;派生类在功能上有很多共性。但是,至关重要的是,每个派生类的变体信息只需要 1 个;没有冗余。所以问题是 - 如何尽可能干燥。请注意,此示例中的 BaseClass 将具有对 Container 进行操作的方法。在架构中可能有一个 FlyWeightDerivedClassFactory 进一步实例化新的 DerivedClasses 来解决这个问题,但这会取消封装。

考虑创建一个通用 Container 类,它为基类提供一个接口来调用其操作:

interface IContainer {
    void Operation1();
    void Operation2();
}

class Container<T> : IContainer where T : BaseClass {
    private HashSet<T> _internalContainer;

    ...
}

class BaseClass {
    protected IContainer container;
}

class DerivedClass : BaseClass {
    DerivedClass()  {
        container = new Container<DerivedClass>(); 
    }
}

所有特定于类型的操作都应该是内部的Container<T>(或必要时从它派生的类),而接口方法是类型无知的。

于 2013-08-12T18:48:12.730 回答