假设你有一个由“父母”和“孩子”组成的数据结构,父母和孩子之间的引用是相互的:
借用a previous post,以下代码写入保证父母与子女之间的相互参照(不包含滥用反映):
public static class Base
{
private interface IParent
{
List<Child> Children { get; }
}
public class Parent : IParent
{
private readonly List<Child> _children = new List<Child>();
public readonly ReadOnlyCollection<Child> Children = _children.AsReadOnly();
List<Child> IParent.Children { get { return _children; } }
}
public class Child
{
private Parent _parent;
public Parent Parent
{
get
{
return _parent;
}
set
{
if(value == _parent)
return;
if(_parent != null)
{
((IParent)_parent).Children.Remove(this);
_parent = null;
}
if(value != null)
{
((IParent)value).Children.Add(this);
_parent = value;
}
}
}
}
}
现在假设你想要一个类似的结构,但你也想要类型安全。也就是说, TParent
的实例只能引用TChild
的实例, TChild
的实例只能引用TParent
的实例
我提出了这个解决方案:
public static class Base<TParent, TChild>
where TParent : Base<TParent, TChild>.Parent
where TChild : Base<TParent, TChild>.Child
{
private interface IParent
{
List<TChild> Children { get; }
}
public class Parent : IParent
{
private readonly List<TChild> _children = new List<Child>();
public readonly ReadOnlyCollection<TChild> Children = _children.AsReadOnly();
List<TChild> IParent.Children { get { return _children; } }
}
public class Child
{
private TParent _parent;
public TParent Parent
{
get
{
return _parent;
}
set
{
if(value == _parent)
return;
if(_parent != null)
{
// Oh no, casting!
((IParent)_parent).Children.Remove((TChild)this);
_parent = null;
}
if(value != null)
{
// Oh no, casting!
((IParent)value).Children.Add((TChild)this);
_parent = value;
}
}
}
}
}
虽然这有效,但{strong> Child
在TChild
内投放到Child.Parent.set
的点让我有点担心。虽然我不确定是否有办法使用这个抛出InvalidCastException
的类,但它可能仍然无法破解。
有更清洁的方法来实现这种效果吗?
答案 0 :(得分:6)
首先,问题Generics and Parent/Child architecture的答案对于与您的父母子女关系的通用建模类似的架构有一些想法。
其次,我强烈建议不要使用这种模式。它令人困惑,可以说是滥用泛型,它实际上并不是类型安全的。见https://blogs.msdn.microsoft.com/ericlippert/2011/02/03/curiouser-and-curiouser/
第三,你的态度是编译器可能错误地认为需要进行类型转换,并且可能导致它失败,这种态度可能会导致你将来遇到麻烦。我建议相反默认情况下相信编译器是正确的,因为它说你的程序不是类型安全的,并且相信如果你认为编译器是错误的,你可能还没有想到它:
class Car : Base<Car, Wheel>.Parent { ... }
class Wheel : Base<Car, Wheel>.Child { ... }
class AnotherWheel : Base<Car, Wheel>.Child { ... }
...
new Car() blah blah blah
new Wheel() blah blah blah
the wheel is the child of the car, blah blah blah
and what happens when you make a Car the parent of an AnotherWheel?
编译器完全正确地说需要强制转换;我们无法断定转换是有效的,因为它不是。
第四,如何解决这个问题而不诉诸滥用实际上并非类型安全的泛型?
有办法做到这一点,但我不认为你会喜欢它们。例如:
可以使用通用接口协方差构造这样的系统,使得不需要演员表;这样做是为了锻炼。
作为一个热身练习,请考虑如何为通用不可变IStack<T>
构造一个接口,这样当你将Turtle推入一堆Tigers时,它就变成了一个不可变的动物堆栈。 / p>
总的来说,我认为最好不要尝试在C#类型系统中捕获这种限制。