2010-08-15 2 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 { } 

мне нужно иметь возможность определить ExtraNode, ExtraElement и ExtraDocument так, что они являются эквивалентом копирования кода выше, добавив несколько дополнительных полей к 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<T>, Element<T>, Document<T>), а затем использовать их, чтобы определить новые классы (ExtraNode : Node<MyExtraClass> и так далее), но она по-прежнему приводит к проблеме (ExtraElement is ExtraNode) == false потому (Element<MyExtraClass> is Node<MyExtraClass>) == false.

Итак, каков наилучший способ достижения этого, без копирования/вставки всего документа и изменения вручную? Мне нужно будет это делать не один раз.

Edit: уточнить, что мне нужно, чтобы быть в состоянии сделать, мне нужен следующий код для работы:

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

Edit 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: Элемент, IExtraElement и IExtraElement будут определены как IExtraElement: IExtraNode. С помощью этой установки (ExtraElement является IExtraNode) == true. –

+0

@ Кирк благодарит за предложение, но мне нужно, чтобы ExtraElement был как ExtraNode, а не IExtraNode. Чтобы проиллюстрировать это, я должен уметь: ExtraNode = someExtraElement ?? someExtraNode. – manixrock

+0

Правильно, поэтому, если вы действительно используете интерфейсы, вы, вероятно, захотите использовать их правильно и последовательно. Другими словами, вы больше не хотите объявлять ExtraNode/ExtraElement/и т. Д. как их конкретные типы - все ваши переменные всегда будут введены в интерфейс. Затем, поскольку «ExtraNode» будет иметь тип IExtraNode, вы можете сделать: ExtraNode = someExtraElement ?? someExtraNode. –

ответ

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 

В ответ на ваш Edit # 2: Да, вы должны дублировать некоторые из реализаций, но не так много. В частности, вам не нужно дублировать что-либо в ExtraNode (вы можете просто наследовать от Node). Если «лишние» вещи не так много, вы можете дублировать только дополнительный материал, то есть наследовать ExtraElement от Element (а не от ExtraNode) и т.д.

Что касается проблемы вы упомянули здесь:

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

I верьте, что это должно быть 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, который содержит всю дополнительную функциональность и содержит ExtraNode, ExtraElement и ExtraDocument. Все поля имеют тип Extra. Вы можете сделать это поле общедоступным или написать однострочные методы перенаправления для всех своих функций. Это поистине минимальное дублирование, учитывая отсутствие множественного наследования.

+0

См. Мою вторую редакцию. – manixrock

+0

@manixrock - Отредактировал мой ответ. – Timwi

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

Вы можете создавать различные Примеси, чтобы получить другой обмен зернистость.