Производные классы абстрактного класса в массиве - PullRequest
2 голосов
/ 23 января 2012

Всем доброго времени суток.

У меня вопрос о создании и использовании производных классов некоторого базового абстрактного класса.

Я не совсем понимаю, как это работает.

Моя цель: создать базовый класс, который будет определять, какие функции должны быть, а затем создавать различные производные классы.Затем создать массив базового класса и создать в нем различные производные классы.

Например:

abstract class UIElement
{
    //constructor
    abstract public UIElement();
    //draw element
    abstract public void Draw(int mouseX, int mouseY);
    //checks if the element is pointed at by mouse
    abstract public Boolean IsPointed(int mouseX, int mouseY);
    //defines what happens when clicked
    abstract public void Click(int mouseX, int mouseY);
    //destructor
    abstract public ~UIElement();
}

Затем создать несколько производных классов и сделать так:

UIElement[] qwe = new UIElement[3];
qwe[0] = new UIElementButton();
qwe[1] = new UIElementLabel();
qwe[2] = new UIElementSomethingElse();

1) Как правильно сделать это, потому что я не могу найти окончательный ответ или статью, которая бы это объяснила ...

2) О переменных класса, где они должны быть объявлены: базовый класс или производный?

3) Что, если разные производные классы имеют разные переменные, как к этому следует обращаться?

4) Могут ли производные классы иметь разные конструкторы, включая разные аргументы?

Ответы [ 3 ]

6 голосов
/ 23 января 2012

В комментариях ниже вопросник ищет советы по передовой практике написания иерархий наследования с абстрактными классами

С точки зрения общей передовой практики и правильности вы должны заявитьваш базовый класс UIElement без абстракции от конструктора и без финализатора.например,

public abstract class UIElement
{
    //constructor
    protected UIElement();
    //draw element
    public abstract void Draw(int mouseX, int mouseY);
    //checks if the element is pointed at by mouse
    public abstract bool IsPointed(int mouseX, int mouseY);
    //defines what happens when clicked
    public abstract void Click(int mouseX, int mouseY);
}

Затем вы можете реализовать производные классы следующим образом

public class UIElementButton : UIElement
{
    // Optional - create parameterless constructor and
    // explicitly call base constructor
    public UIElementButton() : base()
    {
    } 

    // Mandatory - must override abstract methods
    public override Draw(int mouseX, int mouseY)
    {
    }

    // Mandatory - must override abstract methods
    public override Boolean IsPointed(int mouseX, int mouseY)
    {
    }

    // Mandatory - must override abstract methods
    public override void Click(int mouseX, int mouseY)
    {
    }
}

Вы правы, если вы определите производные классы в соответствии с вышеприведенным, вы можете создавать элементы и сохранять их в массиве.базового типа следующим образом:

UIElement[] qwe = new UIElement[3]; 
qwe[0] = new UIElementButton(); 
qwe[1] = new UIElementLabel(); 
qwe[2] = new UIElementSomethingElse(); 

qwe[0].Click(1, 2); // Invokes UIElementButton.Click
qwe[1].Draw(3, 4);  // Invokes UIElementLabel.Draw

Вы также должны знать, что в WPF определен класс UIElement.Это не будет проблемой, если вы определите пространство имен для своего типа, но, возможно, рассмотрите более явное имя типа, такое как BaseElement, чтобы отличить ваш пользовательский тип.


По поводу ваших запросов в комментариях:

Дополнительные советы по использованию абстрактных классов см. Все об абстрактных классах - Codeproject ,- Где должны быть объявлены переменные класса: базовый класс или производный?- Что, если разные производные классы имеют разные переменные, как это следует учитывать?- Могут ли производные классы иметь разные конструкторы, включая разные аргументы?

Это действительно тема для различных вопросов или собственного исследования в Google, однако в качестве начала я могу сказать:

  1. Переменные класса должны быть объявлены в области, где онинеобходимы.Это означает, что если у вас есть переменная Guid _uiElementId и вы хотите, чтобы к ней обращались производные типы, она должна быть объявлена ​​в базовом классе и помечена как protected.Если у вас есть переменная в UIElementButton с именем Point _mousePoint, и она нужна только кнопке UIElement, оставьте ее там и отметьте как private

  2. Это нормально - любой тип может видеть свой собственныйпеременные и защищенные переменные из базовых классов.Просто убедитесь, что имена не конфликтуют

  3. Да, они могут, например.


 public class UIElementButton : UIElement    
 {    
    private string _buttonText; 

    // Overloaded ctor
    public UIElementButton(string buttonText) : base()
    {
        buttonText = buttonText;
    } 

    // Default constructor. Mark as private or protected 
    // to hide from external API
    public UIElementButton() : base()
    {
    } 
}

С уважением,

3 голосов
/ 23 января 2012

Если я правильно угадаю, ОП происходит из C ++ фона.Существует множество параллелей между C ++ и C #, но не все функции соответствуют 1: 1.

Если вы хотите, чтобы ваш "базовый класс" просто установил допустимые операции для всех потомков, то есть он не имеет кода, вы должны использовать интерфейс:

public interface IUIElement
{
  //draw element
  void Draw(int mouseX, int mouseY);
  //checks if the element is pointed at by mouse
  bool IsPointed(int mouseX, int mouseY);
  //defines what happens when clicked
  void Click(int mouseX, int mouseY);
}

Нет необходимости в конструкторе или деструкторе или в явном объявлении методов как открытых, потому что интерфейс не может содержать частные методы / свойства, которые откладываются доклассы, которые его реализуют.

После этого вы можете объявить ваши классы следующим образом (Visual Studio может заместить их для вас):

public class UIElementButton : IUIElement
{
  public UIElementButton()
  {
     ...
  }

  void Draw(int mouseX, int mouseY)
  {
     ...
  }

  bool IsPointed(int mouseX, int mouseY)
  {
     ...
  }

  void Click(int mouseX, int mouseY)
  {
  }
}

Остальная часть вашего кода (с массивом) выглядитхорошо, однако, я бы предпочел список вместо массива:

List<IUIElement> qwe = new List<IUIElement>();
qwe.Add(new UIElementButton());
qwe.Add(new UIElementLabel());
qwe.Add(new UIElementSomethingElse());

// some sample usages
foreach(IUIElement element in qwe)
{
   element.Click();
}

qwe[0].Click() //invokes UIElementButton.Click();

//gets all pointed elements
var pointedElements = qwe.Where(e => e.IsPointed(x,y));
0 голосов
/ 23 января 2012

Из вашего вопроса не ясно, что это проблема, но ваш код не скомпилируется.В C # у вас не может быть abstract конструктора.И финализатор не может быть ни public, ни abstract.Нет необходимости иметь какой-либо из них, вы можете просто удалить их.

И вам, вероятно, вообще не следует использовать финализатор, если вы не хотите распоряжаться каким-либо неуправляемым ресурсом.И если вам это нужно, вы, скорее всего, тоже захотите внедрить IDisposable.

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