正如标题所说,我想知道是否有可能。
我有一个节点类,指向同一数据结构中的另一个节点。
class DataStructure<T, N>
where T : IComparable<T>
where N : Node<T> {
N RootNode;
// More code to follow, etc.
}
class Node<T>
where T : IComparable<T> {
T value;
Node<T> NextNode;
Node<T> GetLastNode() {
Node<T> current = this;
while (this.NextNode != null) {
current = current.NextNode;
}
return current;
}
// etc.
}
我希望能够扩展Node类,以获得DataStructure
的某些通用版本的更多信息。例如:
class AdvancedNode<T> : Node<T>
where T : IComparable<T> {
int Height;
int Size;
// etc.
}
问题在于我尝试关注NextNode
链接。
DataStructure<char, AdvancedNode<char>> d = new DataStructure<char, AdvancedNode<char>>();
d.RootNode = new AdvancedNode<char>();
d.RootNode.NextNode = new AdvancedNode<char>();
AdvancedNode<char> y = d.RootNode.NextNode; // TYPE ERROR! Will not compile
此外,我想这样做是为了不可能做到这样的事情:
DataStructure<char, AdvancedNode<char>> d = new DataStructure<char, AdvancedNode<char>>();
d.RootNode = new AdvancedNode<char>();
d.RootNode.NextNode = new Node<char>(); // This will compile,
// but I don't want it to!
是否有某种方法可以在构建时强制Node.NextNode
与this
的类型相同?我希望能够实现通用数据结构而无需进行转换。可能吗?我使用的是劣质设计模式吗?
答案 0 :(得分:2)
应该使用的一个解决方案是使用“递归泛型”(请参阅此post)。
我们将Node<T>
的定义更改为Node<N, T>
N
必须实施Node<N, T>
...
abstract class Node<N, T>
where N : Node<N, T> // Here is the recursive definition
where T : IComparable<T>
{
T value;
public N NextNode;
public N GetLastNode()
{
N current = (N)this;
while (this.NextNode != null)
{
current = current.NextNode;
}
return current;
}
// etc.
}
然后,您只需将AdvancedNode<T>
的基类更改为Node<AdvancedNode<T>, T>
。
class AdvancedNode<T> : Node<AdvancedNode<T>, T>
where T : IComparable<T>
{
int Height;
int Size;
// etc.
}
以及N
到DataStructure<T, N>
中类型参数Node<N, T>
的约束。
class DataStructure<T, N>
where T : IComparable<T>
where N : Node<N, T>
{
public N RootNode;
// More code to follow, etc.
}
不幸的是,使用“递归泛型”直接实例化是不可能的,因为如果我们想要正确的类型,它需要编写类似:Node<Node<Node<..., T>, T>, T>
的内容。这就是为什么我把它抽象化了。为了拥有一个简单的节点,我创建了一个新类型:
class SimpleNode<T> : Node<SimpleNode<T>, T>
where T : IComparable<T>
{
}
答案 1 :(得分:1)
我没有VS,但对我来说看起来应该是:
Node<T>
更改为Node<TData,TNode>
,并将NextNode
声明为Node<TData,TNode>
。AdvancedNode
声明为AdvancedNode<TData> : Node<TData,AdvancedNode<TData>>
。这将确保子节点与根节点的类型相同。复制2)用于所有其他节点类型。
RootNode
属性移至Node
(其中似乎属于数据的位置)。DataStructure<T,N>
更改为DataStructure<TNode>
(并根据需要从TData
推断TNode
)这将使代码更清晰(在关注点分离方面)并且更易于理解,并且可以帮助您消除使DataStructure依赖于节点类型的需要,这将有助于简化操作。它不会让我认为两种通用类型相互依赖,所以如果可能的话,我的目标是消除它。
答案 2 :(得分:1)
根据您的代码,唯一知道N
的类是DataStructure<T, N>
。在下面的详细说明中,我使用TValue
代替T
,TNode
代表N
。
您的期望是:
约束传递给DataStructure<TValue, TNode>
的节点类型;这样节点的NextNode
也是同一类型。
无需额外施法
然后我能想到的最好的事情是使节点类型成为嵌套泛型类,你仍然能够约束它的类型参数。
由于IComparable<T>
只是值的约束,这与您的问题无关,因此我在以下代码中将其删除,以使代码清晰且易于理解,只需在需要时将其添加回来。
代码
public partial class DataStructure<TValue, TNode>
where TNode: DataStructure<TValue, TNode>.Node<TValue> {
public partial class Node<T> {
public TNode GetLastNode() {
var current=this as TNode;
for(; null!=current.NextNode; current=current.NextNode)
;
return current;
}
public TNode NextNode {
set;
get;
}
public TValue value {
private set;
get;
}
}
public TNode RootNode;
}
public partial class AdvancedNode<TValue>
: DataStructure<TValue, AdvancedNode<TValue>>.Node<TValue> {
int Height;
int Size;
}
请注意原始代码while(this.NextNode!=null) { current=current.NextNode; }
中while循环的表达式会导致NullReferenceException
。
通过上面的代码,现在您可以使用以下测试方法进行测试,该测试方法是原始版本的修改版本:
public static partial class TestClass {
public static void TestMethod() {
DataStructure<char, AdvancedNode<char>> d=
new DataStructure<char, AdvancedNode<char>>();
d.RootNode=new AdvancedNode<char>();
d.RootNode.NextNode=new AdvancedNode<char>();
// type NO error! Will compile
AdvancedNode<char> y=d.RootNode.NextNode;
var rootNode=d.RootNode;
var lastNode=rootNode.GetLastNode();
Console.WriteLine("Is y the last node? "+(lastNode==y));
Console.WriteLine("Is rootNode the last node? "+(lastNode==rootNode));
}
}
由于Node<T>
是引用类型,==
用于比较引用,结果与预期一致。
您可以对之后要声明的其他类做同样的事情。
就个人而言,我考虑使用LinkedList<T>
而不是自己实施。你问,因此答案。
答案 3 :(得分:0)
一种替代方案可能是指定一种反对使用继承的组合。这样做的缺点是扩大特定的成分会更加困难。
例如,使用问题中的类:
class DataStructure<T, D>
where T : IComparable<T> {
Node<T, D> RootNode;
// More code to follow, etc.
}
class Node<T, D>
where T : IComparable<T> {
T value;
D data;
Node<T, D> NextNode;
Node<T, D> GetLastNode() {
Node<T, D> current = this;
while (current .NextNode != null) {
current = current.NextNode;
}
return current;
}
// etc.
}
class AdvancedNodeData {
int Height;
int Size;
// etc.
}
DataStructure<char, AdvancedNodeData> d = new DataStructure<char, AdvancedNodeData>();
d.RootNode = new Node<char, AdvancedNodeData>();
d.RootNode.NextNode = new Node<char, AdvancedNodeData>();
Node<char, AdvancedNodeData> y = d.RootNode.NextNode;