How can I store and use an array of different type

2019-07-29 04:56发布

问题:

The title may need a bit of explanation, so here's what i'm trying to do:

  • Common base type for UI elements, e.g. BasePanel
  • More specialised elements can be defined, derived from BasePanel. For example, buttons and textboxes.
  • A list of elements will be stored (of type BasePanel, so that specialised ones can be stored)
  • This list will be looped through each frame and drawn (ignoring optimisation at the moment)

Example usage:

class UIButton : BasePanel
{
    public override void Draw(blah)
    {
        // Specialised drawing code
    }
}

foreach (BasePanel Element in StoredElements)
{
    Element.Draw(blah);
}

The problem with this is that it won't run the specialised code; it will just run the BasePanel code. How can I improve this so that it will run the specialised code instead? Could I store the type of the element on the BasePanel, and then cast to it at runtime?

I've tried storing the BasePanel in a Container class which stores the original type, but I can't access the method - for example:

foreach(ElementContainer Container in StoredElements)
{
    Type t = Container.OriginalType;
    object el = Container.Element;

    Convert.ChangeType(el, t); //Can't use the returned object!

    t Element = (t)Convert.ChangeType(el, t); //This would be perfect, but it doesn't work.
}

Thanks for any help. This has made my brain explode.

回答1:

It works 100% for me with the following code:

class BasePanel
{
    public virtual void Draw(string blah)
    {
        Console.WriteLine("Base: " + blah);
    }
}

class UIButton : BasePanel
{
    public override void Draw(string blah)
    {
        Console.WriteLine("UIButton: " + blah);
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<BasePanel> list = new List<BasePanel>();

        list.Add(new BasePanel());
        list.Add(new UIButton());
        list.Add(new BasePanel());
        list.Add(new UIButton());
        list.Add(new UIButton());

        foreach (var b in list)
        {
            b.Draw("just a string");
        }
    }
}

What is wrong with yours comes from your drawing code/logic.



回答2:

it looks like your example should work, but you could try extracting the Draw method (and any others you need for polymorphism) into an Inerface, then implementing the interface in each of the classes you want in the list.



回答3:

As DarkSquirrel stated, you can declare the parent class's functions as virtual. This should allow your code to run the specialized code



回答4:

What specialized type are you trying to execute

if you have

class Bar {
  public virtual void Draw() {
    DO STUFF!
  } 
}

class Foo : Bar {
  override public virtual Draw() {
    DO MORE STUFF!
  }
}

then somewhere in your code

Bar[] data = new Bar[] { new Foo(), new Bar()};
foreach (Bar elem in data) {
  elem.Draw();
}

The first elem will execute the overriden code in Foo for the first elem and the code in Bar for the second. Even if you cast an item down the code executed in the methods will still be the code in original object (and elem.GetType() will return Foo for a Foo type.