类派生问题

时间:2010-08-15 15:55:08

标签: c# generics class interface multiple-inheritance

我有以下3个班级:

class Node {
    public Node Parent;

    // Edit: to clarify, each of these classes has many fields and methods
    public void Method1() { /*...*/ }
    public void Method2() { /*...*/ }
    /* ... */
    public void Method30() { /*...*/ }
}
class Element : Node { public Dictionary<string, string> Attributes; }
class Document : Element { }

我需要一种方法来定义ExtraNodeExtraElementExtraDocument,以便它们相当于复制上面的代码,为{{1}添加一些额外的字段使用“Extra”为所有类添加前缀,如下所示:

Node

实现这一目标的最佳方法是什么?我考虑过接口(class ExtraNode { public Node Parent; public int Priority; } class ExtraElement : ExtraNode { public Dictionary<string, string> Attributes; } class ExtraDocument : ExtraElement { } ),然后是ExtraNode : Node, IExtra

我还考虑过将这些类重写为泛型((ExtraElement is ExtraNode) == false),然后使用它们来定义新类(Node<T>, Element<T>, Document<T>等等),但它仍会导致问题{{1因为ExtraNode : Node<MyExtraClass>

那么实现这一目标的最佳方法是什么,无需复制/粘贴整个文档并手动更改?我需要不止一次这样做。

修改以澄清我需要做什么,我需要以下代码才能运作:

(ExtraElement is ExtraNode) == false

编辑2: Kirk关于实现接口的建议(以Timwi的代码为例)在我的案例中是不切实际的,因为每个类都有很多字段和方法(总共大约有8个类),所以我必须为每个'Extra'派生组复制它们中的每一个。例如,这是Node类的外观:

(Element<MyExtraClass> is Node<MyExtraClass>) == false

因此每次都会复制30种方法。这适用于所有其他类。通过这种方法,我会复制这么多,我实际上复制了所有的代码。简单地复制/粘贴整个类,重命名并向其添加额外的字段会更容易。

2 个答案:

答案 0 :(得分:1)

我认为Kirk Woll在评论中的想法几乎是最好的选择。我在代码中遇到了类似的问题,我提出了同样的想法。你真正希望能做的是拥有一个具有多重继承的继承树,而在C#中只有接口允许你这样做。

public interface INode { public INode Parent { get; } }
public interface IElement : INode { public Dictionary<string, string> Attributes { get; } }
public interface IDocument : IElement { ... }

public interface IExtraNode : INode { public int Priority { get; } }
public interface IExtraElement : IExtraNode, IElement { }
public interface IExtraDocument : IExtraElement, IDocument { ... }

现在你想要的所有属性都是真的:

element is INode                // check
document is IElement            // check
extraNode is INode              // check
extraElement is INode           // check
extraElement is IExtraNode      // check
extraDocument is IExtraElement  // check
extraDocument is IElement       // check
extraDocument is INode          // check

回应您的编辑#2:是的,您必须复制某些的实施,但不是很多。特别是,您不需要复制ExtraNode中的任何内容(您可以继承Node)。如果“额外”的内容不多,您只能复制额外的内容,即从ExtraElement(而不是Element)等继承ExtraNode

关于你在这里提到的问题:

public INode Parent { get; } // THIS IS WRONG, THIS SHOULD BE ExtraNode NOT INode

我认为这应该是IExtraNode。您可以通过使用显式实现的属性来解决此问题(我现在假设您ExtraNode派生Node,但仅用于说明解决方案):

// Real code goes here
public IExtraNode Parent { get { return ...; } }

// This calls the property above, and implements the interface property
public INode INode.Parent { get { return Parent; } }

另一个编辑:您还可以声明一个名为Extra的类,其中包含所有额外功能,并且ExtraNodeExtraElement和{{1}所有都有ExtraDocument类型的字段。您可以将此字段设为公共字段,也可以为其所有功能编写单行重定向方法。鉴于缺乏多重继承,这确实是最小的重复。

答案 1 :(得分:0)

您可以尝试使用mixin-like构造来共享必要的代码:

interface MNode {
  MNode Parent { get; }
}
static class MNodeCode {
  public static void Method1(this MNode self) { /*...*/ }
  public static void Method2(this MNode self) { /*...*/ }
  /* ... */
  public static void Method30(this MNode self) { /*...*/ }
}

class Node : MNode {
  public Node Parent { get { ... } }
  MNode MNode.Parent { get { return Parent; } }
}
class Element : Node { public Dictionary<string, string> Attributes; }
class Document : Element { }

class ExtraNode : MNode {
  public ExtraNode Parent { get { ... } }
  MNode MNode.Parent { get { return Parent; } }
}
class ExtraElement : ExtraNode { public Dictionary<string, string> Attributes; }
class ExtraDocument : ExtraElement { }

您可以创建不同的mixin以获得不同的共享粒度。