跳至页脚内容
Iron Academy Logo
C# 常见问题

重构 C# 中的条件 if:与 Derek Comartin 一起避免条件混乱

在 C# 中,if 语句、if else 语句和 switch 语句等条件语句是必不可少的工具。 但是,如果过度使用这些构造,尤其是与枚举绑定时,会发生什么情况呢? Derek Comartin 在他的视频"枚举并不邪恶。 条件无处不在》通过详细的重构,用更简洁、可维护性更强的模式取代了普遍存在的条件逻辑。

在本文中,我们将以 Derek 的时间戳为锚,一步一步地进行推理。 我们还将探讨他的观点如何应用于 C# 中的常见条件模式,如三元运算符、else 语句和 switch-case 结构--强调这些在大型代码库中可能导致问题的地方,以及如何重构以实现更好的设计。

条件 If 爆炸:真正的问题

Derek 首先展示了检查产品类型的 if 语句:

if (productType == ProductType.Template || productType == ProductType.Ebook)
if (productType == ProductType.Template || productType == ProductType.Ebook)

乍一看,上面的条件 if 简单明了。 但 Derek 警告说,这种语句会对给定条件进行评估,然后仅在条件为真的情况下执行代码块--如果这种逻辑到处重复,就会出现问题。

您可能会在另一个方法或类中再次遇到这个块:

if (offeringType == ProductType.Template || offeringType == ProductType.Ebook)
if (offeringType == ProductType.Template || offeringType == ProductType.Ebook)

Derek 解释说,这种模式很快就会在大型系统中传播开来。 多个服务中出现了相同的 if else 逻辑,导致在添加新枚举值时出现不一致和错误。 例如,当您引入一个新的产品类型(如视频)时,会发生什么情况? 您必须记住更新存在此条件表达式的每一个代码块。

重复会增加复杂性

在下面的示例中,Derek 深入探讨了嵌套条件式。 在一个方法中,if else 语句检查相同的枚举,然后将结果传递给另一个方法,后者也包含类似的检查。

语句检查模板或 Ebook,并返回内容,否则返回空。 Derek 指出,这种冗余不仅会使代码变长,还会带来维护方面的隐患。 相同的逻辑被复制到多个文件中,导致控制流混乱。

如果您的系统要求您在每次接触枚举时都添加默认情况,那么您就知道有问题了。

改变我们思考条件句的方式

Derek 建议提出一个更好的问题,而不是不断地用 if else 检查类型:

产品是否具有可下载的特性?

这样才能更好地表达意图。 它使您的代码更具可读性,并减少对枚举的完全依赖。 而不是写一个包含两个条件的 if 语句,如

if (product.Type == ProductType.Template || product.Type == ProductType.Ebook)
if (product.Type == ProductType.Template || product.Type == ProductType.Ebook)

您只需将该逻辑封装在一个模型中并编写即可:

if (product.HasDownloadableResource())
if (product.HasDownloadableResource())

只有当存在可下载资源时,翻译才会返回真实内容,从而减少对复杂条件表达式的需求。

从 If 语句到封装行为

为了解决核心问题,Derek 引入了可下载资源类型。 该类型包括下载 URL 和默认文件名。 它将成为您领域的一流组成部分,而不是依靠 if 语句来推导。

现在,我不再重复这些了:

if (product.Type == ProductType.Template)
{
    // Generate file name
}
else if (product.Type == ProductType.Ebook)
{
    // Generate file name
}
if (product.Type == ProductType.Template)
{
    // Generate file name
}
else if (product.Type == ProductType.Ebook)
{
    // Generate file name
}

你来写

var downloadable = product.GetDownloadableResource();
if (downloadable != null)
{
    Console.WriteLine(downloadable.FileName);
}
var downloadable = product.GetDownloadableResource();
if (downloadable != null)
{
    Console.WriteLine(downloadable.FileName);
}

在翻译过程中,翻译人员必须使用.NET、Java、Python 或 Node js 等开发工具,这样可以大大简化逻辑,不需要 else 语句分支,甚至不需要 switch 语句。

运行时重于编译时:战略转移

Derek 进一步解释了一个重要的设计选择:将逻辑从编译时转移到运行时。这意味着要在运行时查询系统,查看产品是否存在可下载资源。 如果可以,就请付诸行动。 否则,请跳过。

他指出,此举将静态的 if else 逻辑转化为运行时查询。 它可能会增加数据库调用,但会减少嵌套的 if else 逻辑并集中行为。 这样可以提高大规模的可维护性。

为可下载产品使用继承

德里克探索的另一条途径是继承。 您可以创建一个抽象基类 Product,然后定义派生类型,如 Ebook、Template 或 OfflineCourse。

每一个都要覆盖以下方法

public virtual string GetDownloadUrl() { ... }
public virtual string GetDownloadUrl() { ... }

这种方法允许每个产品处理自己的逻辑。 虽然这避免了 switch 语句或多个条件语句,但 Derek 指出,如果您不小心,最终还是会在内部编写条件表达式。

无需继承即可实现更好的封装

如果感觉继承过于繁琐,Derek 建议使用像 DownloadableProduct 这样的显式类型,它包含自己的属性和方法,而不受制于层次结构。

在您的程序中,这可能是这样的

var downloader = new DownloadableProduct(product);
Console.WriteLine(downloader.GetDefaultFileName());
var downloader = new DownloadableProduct(product);
Console.WriteLine(downloader.GetDefaultFileName());

不需要 if else 或 switch 语句来确定行为--每个对象都知道该做什么。

轻量级修复:枚举上的扩展方法

如果您还没有准备好放弃枚举,Derek 会提出一个轻量级的解决方案--创建一个扩展方法:

public static bool IsDownloadable(this ProductType type)
{
    return type == ProductType.Template || type == ProductType.Ebook;
}
public static bool IsDownloadable(this ProductType type)
{
    return type == ProductType.Template || type == ProductType.Ebook;
}

现在不用写

if (product.Type == ProductType.Template || product.Type == ProductType.Ebook)
if (product.Type == ProductType.Template || product.Type == ProductType.Ebook)

您可以将其简化为

if (product.Type.IsDownloadable())
if (product.Type.IsDownloadable())

这样可以集中逻辑,避免重复大括号和代码块。

避免过度使用三元操作符和开关

Derek 还提醒大家不要过度使用三元运算符等速记符号:

string filename = product.Type == ProductType.Template ? "template.pdf" : "default.pdf";
string filename = product.Type == ProductType.Template ? "template.pdf" : "default.pdf";

虽然这是有效的语法,但当逻辑变得复杂时,可能会容易出错,而且难以阅读。 特别是当您的条件评估为 false 时,可能会以微妙的方式分配错误的值。

同样,带有 break 语句和默认情况的 switch 也会落入这个陷阱。与其使用 switch-case 逻辑,不如询问对象的行为。

结论:用更少的杂乱条件实现更智能的控制

总之,Derek 的视频并不是对枚举的攻击,而是对我们如何围绕枚举使用条件 if 结构的批评。 在代码库中散布 if else 和 switch 语句会增加系统的测试、维护和发展难度。

无论您是选择封装、运行时查找、继承还是简单的扩展方法,目标都是一样的:减少条件式,将逻辑移到它该去的地方。

请记住

  • 条件式也不错。

  • 条件杂乱。

  • 简洁的代码不依赖于分散在各个类中的多个 if else 语句。

  • 评估上下文并进行相应的重构。

正如德里克所说,"这取决于你的语境"。但有一点是肯定的:产品并不总是单纯的产品--有时,它是一个重新思考设计的信号。

Hero Worlddot related to 重构 C# 中的条件 if:与 Derek Comartin 一起避免条件混乱
Hero Affiliate related to 重构 C# 中的条件 if:与 Derek Comartin 一起避免条件混乱

分享您的所爱,赚取更多收入

您为使用 .NET、C#、Java、Python 或 Node.js 的开发人员创建内容吗?将您的专业知识转化为额外收入!

钢铁支援团队

我们每周 5 天,每天 24 小时在线。
聊天
电子邮件
打电话给我