限制中间抽象类

时间:2017-07-16 18:24:53

标签: c# inheritance

我有一个抽象类CreatureBehaviour,它提供了TakeTurn方法。这样做的目的是决定生物应该做什么,它应该通过回调提供它的响​​应。这是因为响应可能需要玩家输入,所以它不应该阻止其他进程。

public abstract class CreatureBehaviour {

    public abstract void TakeTurn (Action<TurnAction> response);

}

继承自此,PlayerControl类存储response回调以供以后决策。它的大多数内容都不相关,但最终它必须在玩家做某事时调用response

public class PlayerControl : CreatureBehaviour {

    Action<TurnAction> responseCallback;

    public override void TakeTurn(Action<TurnAction> response) {
        responseCallback = response;
    }

    // Various UI callbacks that can send something to "responseCallback" when appropriate.

}

所有非玩家生物也需要能够发送回调。为了安全起见,我想确保始终触发回调,因此我创建了一个抽象的NonPlayerControl类来确保响应:

public abstract class NonPlayerControl : CreatureBehaviour {

    protected abstract TurnAction TurnResponse ();

    public override void TakeTurn (Action<TurnAction> response) {
        response (TurnResponse ());
    }

}

有了这个,我的所有非玩家生物行为都可以来自NonPlayerControl,只需实现TurnReponse()。编译器保证所有脚本都返回响应,而不是让回调挂起。请注意PlayerControl无法实现TurnResponse(),因为它需要保证立即返回,这会阻止其他进程。

所以我想从NonPlayerControl派生其他类,也许从PlayerControl派生,但我不想意外地从CreatureBehaviour派生另一个类,并且有可能错过回调。

我有什么方法可以“封印”CreatureBehaviour,以便它只能拥有这两个直接的孩子并阻止任何其他孩子?如果没有,我可以在这里使用更好的模式吗?

3 个答案:

答案 0 :(得分:1)

你无法以“正常”的方式做到这一点,但是可以考虑一个选项......

如果您只为<a>类提供私有构造函数,然后在该类中嵌套CreatureBehaviorPlayerBehavior,则可以访问私有构造函数但是没有其他课程......所以没有其他课程可以来自NonPlayerBehavior

更简单的解决方案是:

  • CreatureBehavior中的文档,它不应该直接进行子类化
  • 编写单元测试以验证不是任何其他子类

当然,这只能测试你的代码而不是其他程序集中的代码。如果你不从其他程序集中需要这些,那么将这些类设为内部而不是公共。即使您需要公开所有类,也可以包含在CreatureBehaviorPlayerBehavior中实现的无操作抽象内部方法 - 这将阻止程序集之外的类派生自{{1}因为他们无法实现内部抽象方法。

答案 1 :(得分:1)

只是一个简单的想法,没有进一步测试:你可以使用通用来做限制吗?

public abstract class CreatureBehaviour<T> where T : IPlayerControl, INonPlayerControl {
    // ...
}

然后以下列方式使用它:

public abstract class NonPlayerControl : CreatureBehaviour<NonPlayerControl>, INonPlayerControl {
    // ...
}

public abstract class PlayerControl : CreatureBehaviour<PlayerControl>, IPlayerControl {
    // ...
}

这有点像黑客,但它可能对你的情况有用。

答案 2 :(得分:0)

您不能禁止从CreatureBehavoiur类继承,但您可以通过组合内部密封和受保护访问来限制对TakeTurn方法的访问,并将其放在单独的程序集中:

public  abstract class CreatureBehaviour
{
    protected abstract void TakeTurn(Action<TurnAction> response);
}

public class PlayerControl : CreatureBehaviour
{
    private Action<TurnAction> responseCallback;

    protected override void TakeTurn(Action<TurnAction> response)
    {
        responseCallback = response;
    }

    internal void TurnByPlayer(Action<TurnAction> response)
    {
        TakeTurn(response);
    }

    // Various UI callbacks that can send something to "responseCallback" when appropriate.
}

public abstract class NonPlayerControl : CreatureBehaviour
{
    protected abstract TurnAction TurnResponse();

    protected override void TakeTurn(Action<TurnAction> response)
    {
        response(TurnResponse());
    }

    internal void TurnByNonPlayer(Action<TurnAction> response)
    {
        TakeTurn(response);
    }
}

public sealed class CreatureStearing
{
    public void Turn(PlayerControl control)
    {
        control.TurnByPlayer((action) => {});
    }

    public void Turn(NonPlayerControl control)
    {
        control.TurnByNonPlayer(action => {});
    }
}

现在你可以从其他程序集中的PlayerControl,NonPlayerControl甚至CreatureBehaviour继承,但是你不能将TakeTurn方法用于PlayerControl和NonPlayerControl以外的任何类inance,它们位于不同的程序集中:

public class SomeTest1 : PlayerControl
{
    protected override void TakeTurn(Action<TurnAction> response)
    {
        base.TakeTurn(response);
    }
}

public class SomeTest2 : NonPlayerControl
{
    protected override TurnAction TurnResponse()
    {
        throw new NotImplementedException();
    }

    protected override void TakeTurn(Action<TurnAction> response)
    {
        base.TakeTurn(response);
    }
}

public class SomeTest3 : CreatureBehaviour
{
    protected override void TakeTurn(Action<TurnAction> response)
    {
        throw new NotImplementedException();
    }
}

....

var t1 = new SomeTest1();
        var t2 = new SomeTest2();
        var t3 = new SomeTest3();
        var creatureStearing = new CreatureStearing();
        creatureStearing.Turn(t1);
        creatureStearing.Turn(t2);
        creatureStearing.Turn(t3); // 'Cannot resolve method 'compile error here

当然,您可以通过声明程序集的内部访问来传递此限制,但它需要实现类似CreatureStearing的东西(以及一些努力),但在这种情况下,其他方将确定这是一个黑客。