【问题标题】:class derivation problem类派生问题
【发布时间】:2010-08-15 15:55:08
【问题描述】:

我有以下 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,这样它们就相当于复制上面的代码,在Node 类中添加一些额外的字段,并为所有类添加前缀用“额外”,像这样:

class ExtraNode { public Node Parent; public int Priority; }
class ExtraElement : ExtraNode { public Dictionary<string, string> Attributes; }
class ExtraDocument : ExtraElement { }

实现这一目标的最佳方法是什么?我想到了接口(@98​​7654327@),但后来想到了(ExtraElement is ExtraNode) == false

我也想过把类重写为泛型(Node&lt;T&gt;, Element&lt;T&gt;, Document&lt;T&gt;),然后用它们来定义新的类(ExtraNode : Node&lt;MyExtraClass&gt;等等),但是还是会导致(ExtraElement is ExtraNode) == false的问题,因为(Element&lt;MyExtraClass&gt; is Node&lt;MyExtraClass&gt;) == false .

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

编辑:为了阐明我需要能够做什么,我需要以下代码才能工作:

// this should accept an ExtraNode, an ExtraElement or an ExtraDocument:
void doSomethingWithANode(ExtraNode extraNode) { ... }

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

class INode {
    INode Parent { get; }
    void Method1();
    void Method2();
    /* ... */
    void Method30();
}

class ExtraNode {
    public int Priority { get; } // extra
    public INode Parent { get; } // THIS IS WRONG, THIS SHOULD BE ExtraNode NOT INode
    public void Method1() { ... } // here I have to define the entire method
    public void Method2() { ... }
    /* ... */
    public void Method30() { ... }
}

class SuperNode {
    public string Superpower { get; } // extra
    public INode Parent { get; } // THIS IS WRONG, THIS SHOULD BE SuperNode NOT INode
    public void Method1() { ... } // here I have to define the entire method AGAIN
    public void Method2() { ... } // all methods are identical to the ones in ExtraNode
    /* ... */
    public void Method30() { ... } // this is unnecessary code duplication
}

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

【问题讨论】:

  • 如果您使用接口,那么您可能会有 IExtraNode、IExtraElement 和 IExtraDocument。 ExtraElement 将被定义为 ExtraElement: Element、IExtraElement,而 IExtraElement 将被定义为 IExtraElement: IExtraNode。使用此设置,(ExtraElement is IExtraNode) == true。
  • @Kirk 感谢您的建议,但我需要的是 ExtraElement 作为 ExtraNode,而不是 IExtraNode。为了说明,我需要能够做到:ExtraNode = someExtraElement ??一些额外的节点。
  • 对,所以如果您要实际使用接口,您可能希望正确且一致地使用它们。换句话说,您将不再希望声明 ExtraNode/ExtraElement/等。作为它们的具体类型——所有变量都将始终输入到接口中。然后,由于“ExtraNode”的类型为 IExtraNode,您可以这样做:ExtraNode = someExtraElement ??一些额外的节点。
  • @Kirk 但是接口与节点/元素/文档无关。他们只会有额外的东西。我当然需要对象同时具有来自 Node/Element/Document 和 Extra 的字段。
  • 我不确定您是否可以控制 Node/Element/Document 类。如果你不这样做,你就不走运了。如果这样做,您应该创建这些类实现的 INode/IElement/IDocument 接口。然后您的“IExtra”版本将扩展这些基本接口。从那里你应该做生意。

标签: c# generics class interface multiple-inheritance


【解决方案1】:

我认为 Kirk Woll 在 cmets 中的想法几乎是最佳选择。我在我的代码中遇到了类似的问题,我想出了同样的想法。您真正想要做的是拥有一个具有多重继承的继承树,而在 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 继承)。如果“额外”的东西不多,你可以只复制额外的东西,即从Element继承ExtraElement(而不是从ExtraNode)等等。

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

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

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

// 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 的类,它包含所有额外功能,并且 ExtraNodeExtraElementExtraDocument 都具有类型字段Extra。您可以公开该字段,也可以为其所有功能编写单行重定向方法。鉴于缺乏多重继承,这确实是最少的重复。

【讨论】:

    【解决方案2】:

    您可以尝试使用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 以获得不同的共享粒度。

    【讨论】:

      猜你喜欢
      • 2010-09-10
      • 1970-01-01
      • 1970-01-01
      • 2020-11-24
      • 2010-12-29
      • 2014-04-13
      • 2011-11-12
      • 2017-09-09
      • 1970-01-01
      相关资源
      最近更新 更多