【问题标题】:How to call top class constructor?如何调用顶级构造函数?
【发布时间】:2012-06-23 12:33:57
【问题描述】:

我希望我能正确解释这个问题,这对我来说有点混乱。

我一直在开发类似于 flixel 的游戏库,但使用的是 C# 的 XNA 框架而不是 Flash。现在的班级布局是这样的。

ClxBasic -> ClxObject -> ClxSprite

每个类都有一个构造函数,并调用它下面的类的构造函数。我使用此代码来执行此操作。

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

所以基本上当我创建一个新的 ClxSprite 时,它​​会调用 ClxSprite 构造函数,然后是它下面的所有构造函数(ClxObject 和 ClxBasic)。

我确信有一种更简单的方法可以做到这一点,而且我全神贯注。

然而,我更大的问题实际上是如何正确地派生和覆盖其他类的方法。

问题在于,当创建一个从 ClxSprite 扩展的类时,例如,当调用从最基本的类 (ClxBasic) 覆盖的方法时,它只会调用底部的方法,而不是顶部的方法。

我需要这样做的原因是因为我有一个全局类,它通过将自己添加到 ClxBasic 构造函数中的列表来控制从 ClxBasic 派生的所有对象。

这是一些示例代码。

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

当调用 basic.Update() 时,它会转到底部更新,即位于 ClxBasic 中的更新,尽管对象是 ClxObject 或 ClxSprite 或其他派生类。

我也有一个有限的修复,通过在 foreach 循环中将 ClxBasic 更改为 ClxSprite,您可以正确调用该类的构造函数方法。但是,当基于重写方法的库创建自定义类时,会调用较低的 Update。

但是,限制是您不能添加我没有专门计划的课程。例如,如果我要从 ClxSprite 派生一个 Player 类,并重写 Update() 方法,它将被添加到 GlobalObjects 列表中,但是,永远不会调用它的更新,它将去的最高的是 ClxSprite 的。

想要它的工作方式是,在 Game1.cs 中,我希望能够将 FlxG.Update() 放入 Game.Update() 循环中并能够创建对象并让我的框架处理其余部分。

我希望我有点意思,整个事情感觉就像某种继承的开始,有点让我的大脑受伤。

【问题讨论】:

    标签: c# class inheritance xna


    【解决方案1】:

    要同时调用基类方法作为子类实现的一部分,您可以这样做:

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

    但是子方法不是必需调用基方法。

    另一方面,构造函数总是需要(最终)调用基本构造函数。

    现在,如果您希望在某些处理过程中始终调用基类方法,您可以使用template method pattern。基本上,您的基类有一个非虚拟方法,该方法驱动调用虚拟(或抽象)方法的算法;子类可以覆盖它以创建自己的版本。

    【讨论】:

    • 这是否也调用了它下面的所有方法?你能告诉我如何用三个类而不是两个类来完成吗?所有类都应该调用它下面的构造函数,以使我的更新循环正常工作。
    • 太完美了。谢谢你,一切都应该如此。我非常感谢您的帮助,这让我发疯了。
    【解决方案2】:

    您正确地使用base() 来调用基类的构造函数。至于如何定义构造函数,为什么 Constructor() 是一个单独的方法,而不是不同构造函数的主体?如果您只打算在创建一个类的新实例时调用Constructor(),我建议您将Constructor() 移回您的实际构造函数中,如下所示:

    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
            }
        }
    }
    

    至于能否调用适当的Update() 函数,取决于您的对象的实际类,您可以使用virtualoverride 关键字来完成此操作。你可以这样使用它们:

    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
            }
        }
    }
    

    当您在作为派生自ClxBasic 的类的实例的对象上调用Update() 时,将调用该对象继承链中的顶级Update() 函数。例如:

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

    此外,如果您希望您的 Update() 函数调用其父函数的 Update() 函数,您可以使用 base keyword。 例如:

    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
        }
    }
    

    【讨论】:

    • 感谢您的描述性回答!我之所以有这样的构造函数是因为我当时无法调用下面的构造函数,因为我不知道那个甜蜜的 public ClxObject() : base() 技巧。我只是在发布这个问题之前添加了这一点,所以我并不完全确定它的限制。我在我的库代码中修复了它。再次感谢!
    【解决方案3】:

    根据您的描述,您的目标似乎是实现不同游戏类之间的多态行为。更好的解决方案是定义不同游戏类必须实现的接口。然后,您可以将所有游戏对象放在一个通用容器中,例如数组列表,然后让主游戏循环遍历对象列表并在每次整体更新期间调用每个对象的更新方法。我会设计这样的类:

    interface IUpdatable {
       void doUpdate();
    }
    
    class GameClassA : IUpdatable {
       void doUpdate() { // }
    }
    
    class GameClassB : IUpdatable {
       void doUpdate() { // }
    }
    

    等等

    您的目标是在不同类的对象之间实现多态行为,但不一定要共享数据和通用功能。虽然您也可以通过继承来实现多态性,但在这种情况下,最好通过简单的接口和组合来实现。

    【讨论】:

      猜你喜欢
      • 2016-07-16
      • 2010-12-06
      • 1970-01-01
      • 2021-11-23
      • 2017-09-28
      • 1970-01-01
      • 2021-06-29
      • 2012-10-27
      • 2011-01-24
      相关资源
      最近更新 更多