类派生问题

发布于 2024-09-14 21:52:12 字数 2568 浏览 1 评论 0原文

我有以下 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 类添加一些额外的字段,并为所有类添加“Extra”前缀,如下所示:

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、Element、Document),然后使用它们来定义新类(ExtraNode : Node; 等等),但仍然会导致 (ExtraElement is ExtraNode) == false 的问题,因为 (Elementis Node) ==假

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

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

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

编辑2:柯克关于实现接口的建议(如 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 个方法。这适用于所有其他类别。通过这种方法,我会复制很多内容,几乎复制所有代码。只需复制/粘贴整个类、重命名并向其中添加额外的字段会更容易。

I have the following 3 classes:

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 { }

I need to have a way to define ExtraNode, ExtraElement and ExtraDocument so that they are the equivalent of copying the code above, adding a few extra fields to the Node class, and prefixing all classes with "Extra", like so:

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

What is the best to achieve this? I thought about interfaces (ExtraNode : Node, IExtra), but then (ExtraElement is ExtraNode) == false.

I also thought about rewriting the classes as generics (Node<T>, Element<T>, Document<T>), then using them to define the new classes (ExtraNode : Node<MyExtraClass> and so on), but it still leads to the problem of (ExtraElement is ExtraNode) == false because (Element<MyExtraClass> is Node<MyExtraClass>) == false.

So what is the best way of achieving this, without copy / pasting the entire document and changing by hand? I will need to do this more than once.

Edit: to clarify what I need to be able to do, I need the following code to work:

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

Edit 2: Kirk's suggestion about implementing interfaces (as exemplified by Timwi's code) is impractical in my case because each class has many fields and methods (and there are about 8 classes in total), so I would have to copy each and every one of them for each 'Extra' derivate group. For example this is how the Node classes would look:

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
}

So 30 methods are duplicated every single time. And this applies for all the other classes. By this method I would duplicate so much, I'm practically duplicating all the code. It would be easier to simply copy / paste the entire class, rename and add the extra fields to them.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

小糖芽 2024-09-21 21:52:12

我认为柯克沃尔在评论中的想法几乎是最好的选择。我在代码中遇到了类似的问题,并且提出了相同的想法。您真正想要做的是拥有一个具有多重继承的继承树,而在 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 { ... }

现在您想要的所有属性都是 true:

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 类型的字段。您可以公开该字段,也可以为其所有功能编写一行重定向方法。鉴于缺乏多重继承,这确实是最少的重复。

I think Kirk Woll’s idea in the comments is pretty much the best option. I ran into a similar problem in my code, and I came up with the same idea. What you really want to be able to do is have an inheritance tree with multiple inheritance, and in C# only interfaces allow you this.

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 { ... }

Now all the properties you wanted are true:

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

Responding to your Edit #2: Yes, you would have to duplicate some of the implementations, but not many. In particular, you don’t need to duplicate anything in ExtraNode (you can just inherit from Node). If the “extra” stuff is not much, you can duplicate only the extra stuff, i.e. inherit ExtraElement from Element (instead of from ExtraNode) etc.

Regarding the problem you mentioned here:

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

I believe this should be IExtraNode. You can solve this by using an explicitly-implemented property (I’m assuming now that you’re not deriving ExtraNode from Node, but only to illustrate the solution):

// 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; } }

Yet another edit: You could also declare a class called Extra which contains all the extra functionality, and have ExtraNode, ExtraElement and ExtraDocument all have a field of type Extra. You could either make this field public, or you could write one-line redirect methods for all its functionality. This is truly a minimum of duplication, given the lack of multiple inheritance.

青衫负雪 2024-09-21 21:52:12

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

You could try to use a mixin-like construct to share the necessary code:

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 { }

You can create different mixins to get a different sharing granularity.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文