【问题标题】:calling every class that inherits a base class without knowing the class exists c#在不知道该类存在的情况下调用每个继承基类的类c#
【发布时间】:2014-03-26 23:17:43
【问题描述】:

我正在尝试在 websocket 服务器中实现一个类似于 unity3d 所做的系统。他们有一个名为 MonoBehavior 的基类,所有类都从该基类继承。

这些类具有特定的函数(start、ongui、update),它们会在特定的时间间隔内被调用。

从该类继承的每个类都调用其函数(每帧调用更新)

我想创建一个基类,然后调用它,并让每个包含其中一个函数的类都像 unity3d 一样自动调用。我试过谷歌搜索,但不知道这种类型的类行为被称为什么来获得有用的结果

【问题讨论】:

  • 你确定拥有一个包含所有子类对象的列表不是更好吗?
  • 是的,我确信我正在设计一个框架,使用时会有未知数量的子类

标签: c# class inheritance


【解决方案1】:

为什么不直接实现一个静态管理器类,如下所示:

public static class CallManager
{
    public static HashSet<Callable> callables = new HashSet<Callable>();

    // Other management code...
}

使用一个基类,所有可调用类的子类,它会自动将每个新实例添加到由您的CallManager 管理的可调用对象集:

public abstract class Callable
{
    public Callable()
    {
        OnCreate();
    }

    protected void OnCreate()
    {
        CallManager.callables.Add(this);
    }

    public abstract void Start();
    public abstract void OnGUI();
    public abstract void Update();
}

现在,每当有人创建新的Callable 时,基本的Callable 构造函数将使用新的Callable 更新CallManager。完整的示例程序如下:

class Caller
{
    public static void Main(string[] args)
    {
        Callee1 c1 = new Callee1();
        Callee2 c2 = new Callee2();
        foreach (Callable c in CallManager.callables)
        {
            c.Start();
            c.OnGUI();
            c.Update();
        }
    }
}

public abstract class Callable
{
    public Callable()
    {
        OnCreate();
    }
    protected void OnCreate()
    {
        CallManager.callables.Add(this);
    }
    public abstract void Start();
    public abstract void OnGUI();
    public abstract void Update();
}

public static class CallManager
{
    public static HashSet<Callable> callables = new HashSet<Callable>();
}

public class Callee1 : Callable
{
    public Callee1()
    {

    }
    public override void Start()
    {
        Console.WriteLine("Callee1::Start");
    }
    public override void OnGUI()
    {
        Console.WriteLine("Callee1::OnGUI");
    }
    public override void Update()
    {
        Console.WriteLine("Callee1::Update");
    }
}

public class Callee2 : Callable
{
    public Callee2()
    {

    }
    public override void Start()
    {
        Console.WriteLine("Callee2::Start");
    }
    public override void OnGUI()
    {
        Console.WriteLine("Callee2::OnGUI");
    }
    public override void Update()
    {
        Console.WriteLine("Callee2::Update");
    }
}

【讨论】:

  • 有没有办法做这样的事情而不必通过 new() 实例化子类?有没有办法在命名空间中搜索从基类继承的任何类,然后调用它们?
  • @JacquelineLoriault 为了创建对象,必须调用它们的构造函数。但是,使用上面的代码,框架不需要调用构造函数。该框架将跟踪新的Callable 对象,无论它们在何处初始化。只要在对象子类CallableCallManager 就会维护它们的记录。
【解决方案2】:

可能是这样的:

public static class Caller {
    public static event EventHandler Updating;
    public static Update() {
        var handler = Updating;
        if (handler ¡= null) handler(null, EventArgs.Empty);
    }
 }

public abstract class Base {
    protected Base() {
        Caller.Updating += Caller_Updating;
    }
    void Caller_Updating(object sender, EventArgs e) {
        Update();
    }
    protected abstract Update();
 }

据我了解,您希望所有派生类都调用该方法。这样,当您调用 Caller.Update 时,Base 的所有派生类都会执行它们的 Update 方法。

【讨论】:

  • 派生类将被称为如果它们添加了自己的事件处理程序,这不是您可以强制执行的。此外,由于该事件是公开的,因此任何不相关的第三方也可能正在监听该事件。
  • @dcastro 更新将对所有派生类调用,因为该事件已在所有派生类必须调用的无参数 ctor 中注册。我在某些场景中使用过这种模式并且没有问题。
【解决方案3】:

我相信您指的是Template Method 设计模式,通常也称为钩子。它是许多许多框架的构建块。它的工作原理是这样的:

public abstract class BaseClass
{
    public void Start()
    {
        //execute start logic
        DoThis();
        DoThat();

        //call base class's hook
        OnStart();
    }

    protected abstract void OnStart();
}

您声明了派生类必须实现的抽象方法OnStart。当基类的Start方法被调用时,基类调用hook,然后由派生类执行。

如果你想重写钩子可选,那么你用一个空实现声明方法 virtual,即protected virtual void OnStart() { }

可能还值得一提的是,这是Hollywood Principle 的核心:“不要打电话给我们,我们会打电话给你!”(即,你不打电话给框架的类 - 框架会调用你!)

【讨论】:

  • 你能展示一些实现示例吗?如果我尝试调用 new BaseClass() 会出错
  • @JacquelineLoriault 那是因为BaseClass 是抽象的。您必须创建一个派生自BaseClass 的具体类,并实例化该具体类。如果您从未听说过抽象类,您可能想看看这两个文档:AbstractAbstract and Sealed Classes and Class Members
  • @JacquelineLoriault 您无法实例化抽象基类。只有派生类。您将基于“BaseClass”创建类,然后实例化它们。另一种方法是使用接口:msdn.microsoft.com/en-us/library/87d83y5b.aspx
  • @NebulaSleuth 接口与这个特定问题并没有真正的关系,最终可能会让他感到困惑:/
  • 那么这不会满足我的需要。我已经知道继承,但是这种风格需要每个子类的列表才能通过,这就是我要避免的。更不用说在 Unity 3D 中你不使用覆盖你只是使用 Start() 或 Awake() 我想知道它是否使用某种形式的反射
猜你喜欢
  • 2010-12-24
  • 2015-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-12
  • 1970-01-01
  • 2011-10-04
  • 1970-01-01
相关资源
最近更新 更多