猿问

我应该假设每个拥有的实例都实现 IDisposable 吗?

我正在开发我的小游戏项目,作为学习和练习 C# 的一种方式,但我遇到了一个设计问题。假设我们有以下一组类:


interface IGameState

{

    //Updates the state and returns next active state

    //(Probably itself or a new one)

    IGameState Tick();

}

class Game

{

    public Game(IGameState initialState)

    {

        activeState = initialState;

    }

    public void Tick()

    {

        activeState = activeState.Tick();

    }

    IGameState activeState;

}

Game 基本上是 GameStates 的状态机。我们可以有MainMenuState,LoadingState或SinglePlayingState。但是添加MultiplayerState(代表玩多人游戏)需要一个套接字连接到服务器:


class MultiplayerState : IGameState, IDisposable

{

    public IGameState Tick()

    {

        //Game logic...

        //Communicate with the server using the Socket

        //Game logic...

        //Render the game

        return this;//Or something else if the player quits

    }


    public void Dispose()

    {

        server.Dispose();

    }

    //Long-living, cannot be in method-scope

    Socket server;//Or similar network resource

}

好吧,这是我的问题,我无法将它传递给它,Game因为它不知道它应该处理它,并且调用代码无法轻易知道游戏何时不再需要它。这个类设计几乎就是我到目前为止所实现的,我可以添加IDisposable,IGameState但我认为它不是一个好的设计选择,毕竟不是所有IGameState的都有资源。此外,这个状态机在某种意义上是动态的,任何活动IGameState都可以返回新状态。所以Game真的不知道哪些是一次性的,哪些是一次性的,所以它只需要测试一切。

所以这让我问了几个问题:

  • 如果我有一个类对非密封类型的参数(例如initialState在游戏的 ctor 中)声明所有权,我应该总是假设它可以IDisposable吗?(可能不是)

  • 如果我有一个IDisposable实例,我是否应该通过强制转换到一个没有实现的基础来放弃它的所有权IDisposable?(可能没有)

我从中收集到,IDisposable感觉像是一个非常独特的界面,具有显着的有损(*) 语义 - 它关心自己的生命周期。这似乎与提供有保证但非确定性内存管理的 GC 本身的想法直接冲突。我来自 C++ 背景,所以真的感觉它试图实现 RAII 概念,但是一旦有 0 个引用,就会手动调用 Dispose(destructor)。我并不是说这是对 C# 的咆哮,更像是我错过了某些语言功能?或者也许是 C# 特定的模式?我知道有,using但这只是方法范围。接下来是终结器,它可以确保调用 Dispose 但仍然不确定,还有其他的吗?也许像 C++' 这样的自动引用计数shared_ptr

正如我所说,上面的例子可以通过不同的设计解决(但我认为不应该),但没有回答可能不可能的情况,所以请不要过多关注它。理想情况下,我希望看到解决类似问题的一般模式。

(*) 对不起,也许不是一个好词。但我的意思是,很多接口都表达了一种行为,如果类实现了所述接口,它只会说“嘿,我也可以做这些事情,但如果你忽略我的那部分,我仍然可以正常工作”。遗忘IDisposable不是无损的。我发现以下问题表明 IDisposable 通过组合传播,或者它可以通过继承传播。这对我来说似乎是正确的,需要更多的输入,但是可以。也正是这样MultiplayerState被感染了。但是在我的Game课堂示例中,它也想向上游传播,这感觉不对。

最后一个问题可能是是否应该有任何有损接口,比如它是否是适合这项工作的工具,在这种情况下是什么?或者我知道还有其他常用的有损接口吗?


慕尼黑5688855
浏览 154回答 1
1回答

MYYA

你所有的问题都是有效的讨论;但是,IDisposable如果您将它传递给一个类型,那么当您处于未知状态时,不知道该类型是否会正确处理它。因此,作为一项规则,一次性类型的原始所有者/初始化程序应始终负责处理。所以在你的情况下,实例化的人MultiplayerState也负责处理它。如果您必须实例化它,然后将它传递给GameState并稍后处理它,那么MultiplayerState应该要求的原始所有者以某种方式跟踪它并正确处理它。另外,在实现时,IDisposable我强烈建议将 disposing 添加到类的析构函数中。这是一个故障安全,以防一次性类型没有正确处理或正确实施。例子: public void Dispose() {      server.Dispose();      GC.SuppressFinalize(this); } ~MultiplayerState() => Dispose()我讲这个更在这里,如果你有兴趣。
随时随地看视频慕课网APP
我要回答