数组中抽象类的派生类

本文关键字:派生 抽象类 数组 | 更新日期: 2023-09-27 18:21:12

大家好。

我有一个关于生成和使用某个基本抽象类的派生类的问题。

我不太明白它是怎么工作的。

我的目标是:创建一个基本类,定义应该有哪些函数,然后创建不同的派生类。然后制作一个基类数组,并在其中实例化不同的派生类。

例如:

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) 派生类是否可以有包含不同参数的不同构造函数?

数组中抽象类的派生类

在下面的评论中,提问者正在寻找关于使用抽象类编写继承层次结构的最佳实践的建议

从一般的最佳实践和正确性的角度来看,您应该声明您的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。-类变量应该在哪里声明:基类还是派生的?-如果不同的派生类有不同的变量,该如何处理?-派生类是否可以有包含不同参数的不同构造函数?

这些确实是谷歌上不同问题或自己研究的主题,然而作为一个开始,我可以说:

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

致问候,

如果我猜对了,OP来自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));

从您的问题中还不清楚这是问题所在,但您的代码无法编译。在C#中,不能有abstract构造函数。终结器不能既不是public也不能是abstract。没有必要有任何一个,你可以简单地删除它们。

通常,您可能不应该使用终结器,除非您想处置一些非托管资源。如果需要的话,您很可能也想实现IDisposable