2010-08-15 73 views
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类,并用前缀“额外”的所有类,像这样:

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>),然后用它们来定义新的类(ExtraNode : Node<MyExtraClass>等),但它仍然导致了(ExtraElement is ExtraNode) == false因为(Element<MyExtraClass> is Node<MyExtraClass>) == false问题。

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

编辑:澄清什么,我需要能够做的,我需要下面的代码工作:

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

编辑2:柯克的有关实现接口(通过Timwi的代码作为例证的建议)在我的情况下是不切实际的,因为每个类都有很多字段和方法(总共大约有8个类),所以我必须为每个“Extra”派生组复制其中的每一个。例如,这是节点类的外观:

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个方法每次都重复一次。这适用于所有其他类。通过这种方法,我会重复这么多,我实际上复制了所有的代码。简单地复制/粘贴整个课程,重命名并添加额外的字段会更容易。

+2

如果您使用的是接口,那么您大概会拥有IExtraNode,IExtraElement和IExtraDocument。 ExtraElement将被定义为ExtraElement:Element,IExtraElement和IExtraElement将被定义为IExtraElement:IExtraNode。通过这个设置,(ExtraElement是IExtraNode)== true。 – 2010-08-15 16:05:39

+0

@Kirk感谢您的建议,但我需要ExtraElement作为ExtraNode,而不是IExtraNode。为了说明,我需要能够做到:ExtraNode = someExtraElement? someExtraNode。 – manixrock 2010-08-15 17:21:10

+0

对,所以如果你真的要使用接口,你可能想要正确和一致地使用它们。换句话说,你不会再声明ExtraNode/ExtraElement/etc。作为它们的具体类型 - 您的所有变量将始终输入到界面中。然后,由于“ExtraNode”将是IExtraNode类型,因此您可以这样做:ExtraNode = someExtraElement ?? someExtraNode。 – 2010-08-15 17:30:33

回答

1

我觉得柯克沃尔的意见在评论中几乎是最好的选择。我在代码中遇到了类似的问题,并提出了相同的想法。你真正想要做的是拥有一个具有多重继承的继承树,而在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继承)。如果“额外”的东西并不多,您可以复制只是多余的东西,即继承ExtraElementElement(而不是从ExtraNode)等

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

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

我相信这应该是IExtraNode

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

另一个编辑:您可以通过使用一个明确的实现属性(我假设现在你Node获得ExtraNode,但只是为了说明溶液)解决这个您也可以声明一个名为Extra的类,其中包含所有额外的功能,并且具有ExtraNode,ExtraElementExtraDocument都具有类型Extra的字段。您可以将该字段公开,也可以为其所有功能编写单行重定向方法。考虑到缺乏多重继承,这确实是最低限度的重复。

+0

请参阅我的第二次编辑。 – manixrock 2010-08-15 19:44:18

+0

@manixrock - 编辑我的答案。 – Timwi 2010-08-15 19:58:05

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

您可以创建不同的混入得到一个不同的共享粒度。