假设我有这两个类
class BaseClass
{
protected HashSet<BaseClass> container;
}
class DerivedClass : BaseClass
{
DerivedClass()
{
container = new HashSet<DerivedClass>();
}
}
然后我收到错误:无法转换。
由于每个DerivedClass(应该)都是一个BaseClass,我不太清楚为什么会抛出这个错误,但事实确实如此。
目标是让BaseClass在container
上执行各种操作,只有特别特定的行为与DerivedClass绑定 - 其中要求容器属于HashSet<DerivedClass>
类型。
这个目标通常如何实现?
答案 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>
的内部操作(或者必要时从它派生的类),而接口方法是类型无知的。