Array中抽象类的派生类

时间:2012-01-23 09:38:38

标签: c# arrays abstract-class derived-class

大家好日子。

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

我不太明白它是如何运作的。

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

例如:

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)关于类变量,应该声明它们:base class或derived?

3)如果不同的派生类有不同的变量,应该如何解决该怎么办?

4)派生类可以有不同的构造函数,包括不同的参数吗?

3 个答案:

答案 0 :(得分:6)

  

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

从一般的最佳实践和正确性的角度来看,你应该在构造函数中没有抽象而没有终结器的情况下声明你的UIElement基类。 e.g。

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)来区分您的自定义类型。


关于您在评论中的查询:

有关抽象类使用的进一步建议,请参阅All about abstract classes - Codeproject。 - 应该在哪里声明类变量:基类还是派生?   - 如果不同的派生类有不同的变量,应该如何解决?   - 派生类可以有不同的构造函数,包括不同的参数吗?

这些确实是关于谷歌的不同问题或自己研究的主题,但作为首发我可以说:

  1. 类变量应在需要它们的范围内声明。这意味着如果您有一个变量Guid _uiElementId并希望它被派生类型访问,那么它应该在基类中声明并标记为protected。如果您在UIElementButton中有一个名为Point _mousePoint的变量且只有UIElement按钮需要它,请将其留在那里并标记为private

  2. 没关系 - 任何类型都可以从基类中看到自己的私有变量和受保护变量。只需确保名称不会发生冲突

  3. 是的,例如,他们可以。


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

    致以最诚挚的问候,

答案 1 :(得分:3)

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

不需要构造函数或析构函数,也不需要将方法显式声明为public,因为接口不能包含私有方法/属性,而是延迟到实现它的类。

在此之后,您可以声明您的类(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));

答案 2 :(得分:0)

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

除非你想要处理一些非托管资源,否则你可能不应该使用终结器。如果您需要,那么您很可能也希望实现IDisposable