проблема вывода классов - PullRequest
       22

проблема вывода классов

0 голосов
/ 15 августа 2010

У меня есть следующие 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.

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

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

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

Edit 2: Предложение Кирка о реализации интерфейсов (на примере кода Тимви) нецелесообразно в моем случае, потому что у каждого класса есть много полей и методов (а всего около 8 классов), поэтому я хотел бынеобходимо скопировать каждый из них для каждой производной группы «Extra».Например, вот как должны выглядеть классы 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 методов.И это относится ко всем остальным классам.Этим методом я бы столько дублировал, что практически дублировал весь код.Было бы проще просто скопировать / вставить весь класс, переименовать и добавить к ним дополнительные поля.

Ответы [ 2 ]

1 голос
/ 15 августа 2010

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

0 голосов
/ 13 сентября 2010

Вы можете попробовать использовать миксин-подобную конструкцию для совместного использования необходимого кода:

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

Вы можете создавать разные миксины, чтобы получить различную степень детализации.

...