3

I hope I can explain this problem right, it's a bit confusing for me.

I have been working on a game library similar to flixel but, using C#'s XNA framework instead of Flash. Right now the current class layout is something like this.

ClxBasic -> ClxObject -> ClxSprite

Each class has a constructor and calls the constructor for the class below it. I use this code to do this.

namespace Test
{
    public class ClxBasic
    {
        public ClxBasic()
        {
            Constructor();
        }

        public void Constructor()
        {
            DoSomething();
        }
    }

    public class ClxObject : ClxBasic
    {
        public ClxObject() : base()
        {
            Constructor();
        }
            public void Constructor()
            {
                    DoSomething();
            }
    }

    public class ClxSprite : ClxObject
    {
        public ClxSprite() : base()
        {
            Constructor();
        }
            public void Constructor()
            {
                    DoSomething();
            }
    }
}

So basically when I create a new ClxSprite it calls the ClxSprite constructor, then all the ones below it (ClxObject and ClxBasic).

I'm sure there is an easier way to do this and I am all ears.

However, my bigger issue is actually how to properly derive and override methods from the other classes.

The issue is that when creating a class that extends from ClxSprite, for example, when calling a method that was overridden from the most basic class (ClxBasic), it will only call the bottom method and not the top.

The reason I need to do this is because I have a global class which keeps control of all the objects derived from ClxBasic by adding themselves to a list in the ClxBasic constructor.

Here's some example code.

namespace Test
{
    public static class ClxG()
    {
        public static List<ClxBasic> GlobalObjects; //All objects will be added here by the ClxBasic constructor
            ClxSprite test = new ClxSprite();
            GlobalObjects.Add(test);
        public static Update()
        {
            foreach(ClxBasic basic in GlobalObjects)
                basic.Update(); //Calling this calls ClxBasic.Update() when it should call ClxSprite.Update()
        }
    }
}

When calling basic.Update() it goes to the bottom Update, the one located in ClxBasic, despite the object being a ClxObject or ClxSprite or other derived class.

I have a limited fix as well, by changing the ClxBasic to ClxSprite in the foreach loop, you can call that classes constructor method properly. However, when making custom classes based off of the library who override a method, the lower Update is called.

However, the limit is that you can't add classes I didn't specifically plan for. For example, if I were to derive a class Player from ClxSprite and, override the Update() method, it would get added to the GlobalObjects list but, never have it's update called, the highest it will go is ClxSprite's.

The way I want it to work is, in Game1.cs I want to just be able to put FlxG.Update() in the Game.Update() loop and just be able to create the object and have my framework handle the rest.

I hope I've made a bit of sense, the whole thing feels like some sort of inheritance inception and kind of makes my brain hurt.

4

3 回答 3

1

To also call a base class method as part of a child class implementation, you'd do this:

class Base {
  public virtual void Method() {
    // ...
  }
}

class Derived : Base {
  public override void Method() {
    base.Method();
    // ....
  }
}

class Derived2 : Derived {
  public override void Method() {
    base.Method();
    // ....
  }
}

But the child method is not required to call the base one.

Constructors, on the other hand, are always required to (ultimately) call a base constructor.

Now, if you want a base class method to always be called as part of some processing, you can employ the template method pattern. Basically your base class has a non-virtual method that drives an algorithm that calls virtual (or abstract) methods; which the child classes can override to create their own versions.

于 2012-06-23T12:39:03.427 回答
1

You are properly using base() to call the constructors of your base classes. As for how you are defining your constructors, why is Constructor() a separate method, rather than the body of the different constructors? If you are only planning on calling Constructor() when you create a new insance of one of your classes, I would recommend moving Constructor() back into your actual constructors, like so:

namespace Test
{
    public class ClxBasic
    {
        public ClxBasic()
        {
            // Do Something
        }
    }

    public class ClxObject : ClxBasic
    {
        public ClxObject() : base()
        {
            // Do Something
        }
    }

    public class ClxSprite : ClxObject
    {
        public ClxSprite() : base()
        {
            // Do Something
        }
    }
}

As for being able to call the appropriate Update() function, depending on the actual class of your object, you would accomplish this using the virtual and override keywords. You would use them like so:

namespace Test
{
    public class ClxBasic
    {
        // Define the base function that can be overridden
        // in subclasses.
        public virtual void Update()
        {
            // Do Some Updates
        }
    }

    public class ClxObject : ClxBasic
    {
        // We're overridiung the base function, so we
        // must mark this function as an override.
        public override void Update()
        {
            // Do Some Updates
        }
    }

    public class ClxSprite : ClxObject
    {
        // We're overridiung the base function, so we
        // must mark this function as an override.
        public override void Update()
        {
            // Do Some Updates
        }
    }
}

When you call Update() on an object that is an instance of a class derived from ClxBasic, the top-level Update() function in that objects inheritance-chain will be called. For instance:

ClxBasic clxBasic = new ClxBasic();   // Calls ClxBasic.Update()
ClxBasic clxObject = new ClxObject(); // Calls ClxObject.Update()
ClxBasic clxSprite = new ClxSprite(); // Calls ClxSprite.Update()

In addition, if you want your Update() functions to call the Update() function of their parent, you can use the base keyword. For example:

public class ClxSprite : ClxObject
{
    // We're overridiung the base function, so we
    // must mark this function as an override.
    public override void Update()
    {
        base.Update(); // Will call ClxObject's Update() function
        // Do Some Updates
    }
}
于 2012-06-23T13:37:03.280 回答
0

Based on your description, your goal seems to be to achieve polymorphic behavior among different game classes. A better solution is to define an interface that different game classes must implement. Then you can put all of your game objects in one generic container, such as an array list, and then have the master game loop iterate through the object list and invoke each object's update method the method during each overall update. I would design the classes like this:

interface IUpdatable {
   void doUpdate();
}

class GameClassA : IUpdatable {
   void doUpdate() { // }
}

class GameClassB : IUpdatable {
   void doUpdate() { // }
}

etc.

Your goal is to achieve polymorphic behavior among objects of different classes but not necessarily to share data and common functionality. While you can achieve polymorphism through inheritance as well, it is better achieved in this case through simple interfaces and composition.

于 2012-06-23T13:11:34.410 回答