IRONSOFTWAREHOME

C# 静态变量和方法邪恶吗?Derek Comartin 解释(视频分解)

TH 跳过导航 搜索 创建头像图片 静态变量和方法是邪恶的吗?

Derek Comartin

7分57秒

在 C# 软件开发的世界里,您很可能遇到过 static 关键字--无论是在静态 void Main、静态变量还是静态方法中。 但是,静态总是一个好主意吗? 或者,正如一些开发人员警告的那样,这些工具在大型应用程序中是否存在危险?

为了了解事实真相,我们将通过 CodeOpinion.com 的 Derek Comartin 提供的"Static Variables & Methods are Evil?"详细视频来了解静态成员可能成为问题的细微原因,但并非总是如此。 我们将使用他的例子和时间戳来指导这次深入研究。

是什么导致静态方法出现问题?

在视频的开头,Derek 介绍了一个名为 Is18YearsOrOlder 的静态方法。 此方法获取 DateTime birthDate 并检查某人是否年满 18 岁。它使用 DateTime.UtcNow 来比较当前日期。 很简单吧?

但正如德里克所指出的,这种方法是非确定性的。 在 0:50 处,他强调使用 DateTime.UtcNow 意味着该方法将根据运行时间返回不同的结果。 这是单元测试中的一个主要问题,会导致代码中出现意外行为。

在本例中,虽然该方法看起来像一个纯函数,但实际上并非如此。 Derek 解释说,纯方法每次使用相同参数调用时必须返回相同的值。 但在这里,当前日期不断变化,因此返回值也在不断变化。

这说明了公共静态方法虽然方便,但如果依赖于实时数据或应用领域状态,就会带来副作用。

使静态方法可测试、可预测

Derek 的下一点至关重要:我们可以通过移除对 DateTime.UtcNow 的依赖来解决非确定性问题。 Derek 没有介绍如何使用静态类或接口实现来注入时间提供程序,而是介绍了如何使用静态类或接口实现来注入时间提供程序。 这使得函数具有确定性--每次传递相同的输入都会得到相同的输出。

在他的 PlaceOrder 类中,他引入了一个假日期提供者,这样他就可以测试在周五处理的订单是否可以获得 50% 的折扣。 这样可以避免与系统时间挂钩的硬编码逻辑,使方法更加可靠和可测试。

通过隔离行为和避免在业务逻辑中直接引用静态方法,Derek 展示了如何在保持可测试性的同时保留简洁的代码。

紧密耦合和静态方法

Derek 在此提醒,依赖静态方法往往会带来紧密耦合。 如果您直接使用 DateTime.UtcNow,您将受该实现的约束 - 您不能覆盖或模拟它。

这是一个问题,因为像这样的静态成员在您的应用程序中是全局性的。 如果您的代码库大量使用静态字段或静态属性,就很难改变行为或注入依赖关系,从而破坏了面向对象编程的关键原则。

由于不能像实例变量或注入服务那样用不同的实现来替代静态字段,因此也就失去了灵活性。

静态变量的全局状态问题

现在,Derek 将重点转向静态变量,这也是对话变得严肃的地方。

他介绍了一个在 Global 类中使用静态缓存的例子。 他解释说,静态变量的最大问题是状态未知。 在运行时,您无法确定静态字段是否已被初始化。 当涉及到可变的静态 int 或字符串名称时,这种不可预测性尤其具有风险。

当开发人员假定跨线程共享的变量只有一个副本,而忘记考虑线程安全时,这种情况就会变得更糟。

多线程代码中的线程安全和静态字段

Derek 还提出了另一个问题:在多线程环境中使用静态变量。 他举例说明了静态 List<Customer> 与 Parallel.For. 代码崩溃是因为静态字段不是线程安全的。

为了解决这个问题,他改用了 ConcurrentBag<Customer>,这是 .NET 中的一种线程安全集合。 这样可以跨多个线程安全地访问静态数据。

他的观点很明确:如果要跨线程使用静态变量,请确保它们是线程安全的。 否则,您的程序可能会出现不可预知的行为,甚至崩溃。

静态方法的安全使用

然后,德里克分享了静态方法的安全有效用法:一个简单的实用方法 MilesToKilometers。 它接收 int 里数,转换后返回 double 值。 这种方法是确定性的--对于相同的 int 值,总是得到相同的结果。

这种方法不依赖非静态字段,不改变共享数据,也不涉及任何未知状态。 这是一个很好的例子,说明了如何在 C# 中正确使用静态关键字。

在 .NET 环境中理解静态语言

在 C# 中,static 关键字可用于类、字段、方法、构造函数和属性。 Derek 间接涉及了以下概念:

  • 静态类:不能实例化且只能包含静态成员的类。

  • 静态字段:使用静态关键字声明--每个应用领域只存在一个副本。

  • 静态构造函数:仅在首次访问类时运行一次。

  • Static void Main:大多数 C# 应用程序的入口点,展示了静态方法的重要性。

  • 静态 int、静态字符串:静态字段的示例,这些字段存储类的所有实例所共有的数据,或者实际上根本不需要实例。

与每次创建对象时都要运行的实例构造函数不同,静态构造函数只初始化一次类级资源。

这种区别有助于开发人员决定何时使用实例变量与静态变量,或何时使用属性访问器封装共享成员变量。

Derek 的最终收获

Derek 总结了开发人员对静态成员持谨慎态度的主要原因:

  • 紧密耦合--您将无法摆脱静态方法或字段的行为。

  • 非确定性行为--难测试,易破坏。

  • 全局可变状态--您不知道值是什么,也不知道是谁改变了它。

  • 并发问题--多线程代码中对共享数据的不安全访问。

然而,正如 Derek 所说,静态并不邪恶。 如果使用得当,特别是在实用功能、共享常量或真正的全局设置中,它的功能会非常强大。 您只需小心管理状态,避免依赖于易变或系统特定的行为。

结论

静态变量和方法在 C# 中是一把双刃剑。 正如 Derek Comartin 清楚解释的那样,这些工具本质上并不坏--但在使用时需要深思熟虑。 当您需要共享数据或不依赖于对象状态的功能时,请使用静态字段和静态类。 但要避免将它们用于依赖时间、系统状态或需要灵活性的事情上。

因此,在创建对象或访问静态字段之前,请考虑范围、可测试性、线程安全性,以及代码是需要一个副本还是多个实例。

在他的 CodeOpinion YouTube 频道上观看 Derek Martin 的完整视频。 您会发现更多关于简洁架构、软件设计和真实 C# 应用的见解。

Earn More by Sharing What You Love

Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Let's Stay in Touch!

Join our newsletter, you’ll get exclusive access on article updates. We value your privacy

Key in blue circle

立即获取免费的 30 天试用版密钥

Your trial license will be sent to your email address

无任何限制。100% 解锁。无需信用卡。

bullet_checked无需信用卡或创建账户无任何限制。100% 解锁。无需信用卡。
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried IronPDF
获取您的无义务咨询
填写下面的表格或通过sales@ironsoftware.com
您的资料将始终保密。
深受全球数百万工程师信赖
Iron Software 的客户徽标
立即获取您的免费30 天试用密钥
无需信用卡或创建账户