014.讨论资源处理,组合性与同步

到目前为止,我们的代码给出了Penny先生在本章开头描述的需求的解决方案。从功能上讲,代码完成了它需要做的一切。但是你对此有什么感觉?可读吗?它似乎是可维护的吗?它容易扩展吗?我想让你注意几件事。

代码散射

让我们从代码的分散性开始。众所周知,分散的代码会使程序更难维护、审查和测试。在我们的示例中,程序的主要逻辑位于离事件注册“很远”的OnStockTick事件处理程序中:

class StockMonitor
{
    public StockMonitor(StockTicker ticker)
    {
        ... 
        ticker.StockTick += OnStockTick;//注册事件
    }

    //处理事件
    void OnStockTick(object sender, StockTick stockTick)
    {
        ... 

    //取消注册和清理
    public void Dispose()
    {
        ...  //取消注册并清理类,无论是否与事件相关。
    }
}

处理多个事件(甚至更多)的类是很常见的,每个事件都在自己的事件处理程序中,你可能会开始忽略哪些与哪些相关:

很多时候,开发人员选择将事件处理程序注册更改为lambda表达式,例如:
anObject.SomeEvent += (sender, eventArgs)=>{…};

尽管您将事件处理程序逻辑移到了注册中,但还是为资源清理添加了一个bug。如何注销?-=运算符希望您注销已注册的同一委托。lambda表达式只能按以下方式取消注册:
eventHandler = (sender, eventArgs)=>{…};
anObject.SomeEvent += eventHandler;
:
anObject.SomeEvent -= eventHandler;

这看起来很不干净,所以如果需要注销eventHandler,现在需要将其保存为成员,这就引出了下一点。

资源处理
事件的注销以及为支持代码(如字典)而添加的其他资源清理都发生在Dispose方法中。这是一种使用得很好的模式,但更常见的情况是,开发人员忘记释放代码使用的资源。尽管C#和。NET作为一个整体进行管理并使用垃圾收集,很多时候您仍然需要适当地释放资源以避免内存泄漏和其他类型的错误。事件通常是未注册的,这是内存泄漏的主要原因之一。原因(至少对一些人来说)是,我们注销的方式对许多开发人员来说并不自然,而且决定正确的注销地点和时间并不总是那么简单——尤其是因为许多开发人员更喜欢使用lambda风格来注册事件,正如我之前所述。除了事件本身,您还添加了代码和状态管理(如我们的字典)来支持您的逻辑。更多类型的应用程序处理相同的场景,例如过滤、分组、缓冲,当然还有它们带来的东西的清理。这就引出了下一点。

可重复性和可组合性
对我来说,我们的逻辑也是可重复的。我发誓我在过去的应用程序中编写了这段代码(或类似的代码),通过一个键保存以前的状态,并在每次更新时更新它,我敢打赌您也有同样的感觉。此外,我还觉得这段代码是不可组合的,条件越多,看到的内部if语句就越多,代码的可读性就越差。在应用程序中看到这种代码是很常见的,并且由于其箭头状的结构,它变得更难理解和遵循它的功能:

if (some condition)
{
 if (another condition)
 {
 if (another inner condition)
 {
 //some code
 }
 }
}
else
{
 if (one more condition)
 {
 //some code
 }
 else
 {
 //some code
 }
}

组合

组合是指由更简单的结构组成复杂结构的能力。这个定义类似于数学中的定义,在数学中,你可以从一组其他函数组成一个复杂的表达式:f(x)=x2+sin(x),组合还允许将一个函数用作另一个函数的实参:
g(x) = x + 1
f(g(x)) = (x + 1)2 + sin(x + 1)
在计算机科学中,我们使用组合来表达具有更简单函数的复杂代码。这使我们能够进行更高的抽象,专注于代码的目的,而较少关注细节,从而更容易掌握。

如果给您的代码提供了新的需求,例如通过查看两个以上连续的事件来计算变更比率,那么您的代码将不得不发生巨大的变化。如果新需求是基于时间的,例如查看一个时间间隔内的变更比率,那么变更将更加剧烈。

同步

同步是开发人员容易忘记的另一件事,它会导致与我们遇到的问题相同的问题:由于计算值不正确而导致的不可靠代码,以及在使用非线程安全类时可能发生的崩溃。同步就是要确保如果多个线程同时访问相同的代码(实际上,不一定是并行的,因为可能涉及上下文切换),那么只有一个线程可以访问。锁是实现同步的一种方法,但是存在其他方法,并且需要知识和注意。

编写线程不安全的代码很容易,但编写带有导致死锁或饥饿的锁的代码更容易。这些类型的bug的主要问题是很难找到。您的代码可能会运行很长时间(从字面上讲),直到遇到崩溃或其他错误。

这么小的程序有这么多要点,难怪人们说编程很难。是时候看看Rx的伟大之处,以及它如何使我们讨论的问题更容易克服。让我们看看Rx的方式,并开始将Rx添加到您的应用程序中。

——未完待续

——重庆教主(QQ23611316) 2024.05.19

——WPF中文网 wpfsoft.com

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值