模板参数中的“协方差”

时间:2013-08-12 17:54:40

标签: c#

假设我有这两个类

class BaseClass 
{
    protected HashSet<BaseClass> container;
}


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

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

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

目标是让BaseClass在container上执行各种操作,只有特别特定的行为与DerivedClass绑定 - 其中要求容器属于HashSet<DerivedClass>类型。

这个目标通常如何实现?

4 个答案:

答案 0 :(得分:3)

每个DevrivedClass都是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>(); 
  }
}

这看起来有点奇怪,但是再一次看起来很奇怪一个类包含相同类型的对象列表,所以也许在你的真实场景中它是有意义的。

答案 1 :(得分:2)

你能做的是:

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


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

答案 2 :(得分:0)

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

修改

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

答案 3 :(得分: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>的内部操作(或者必要时从它派生的类),而接口方法是类型无知的。