与类实例具有相同类型的成员

时间:2013-07-25 22:22:55

标签: c# inheritance data-structures types

正如标题所说,我想知道是否有可能。

我有一个节点类,指向同一数据结构中的另一个节点。

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.NextNodethis的类型相同?我希望能够实现通用数据结构而无需进行转换。可能吗?我使用的是劣质设计模式吗?

4 个答案:

答案 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.
}

以及NDataStructure<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,但对我来说看起来应该是:

  1. Node<T>更改为Node<TData,TNode>,并将NextNode声明为Node<TData,TNode>
  2. AdvancedNode声明为AdvancedNode<TData> : Node<TData,AdvancedNode<TData>>
  3. 这将确保子节点与根节点的类型相同。复制2)用于所有其他节点类型。

    1. RootNode属性移至Node(其中似乎属于数据的位置)。
    2. DataStructure<T,N>更改为DataStructure<TNode>(并根据需要从TData推断TNode
    3. 这将使代码更清晰(在关注点分离方面)并且更易于理解,并且可以帮助您消除使DataStructure依赖于节点类型的需要,这将有助于简化操作。它不会让我认为两种通用类型相互依赖,所以如果可能的话,我的目标是消除它。

答案 2 :(得分:1)

根据您的代码,唯一知道N的类是DataStructure<T, N>。在下面的详细说明中,我使用TValue代替TTNode代表N

您的期望是:

  1. 约束传递给DataStructure<TValue, TNode>的节点类型;这样节点的NextNode也是同一类型。

  2. 无需额外施法

  3. 然后我能想到的最好的事情是使节点类型成为嵌套泛型类,你仍然能够约束它的类型参数。

    由于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;