函数中内部作用域块的使用是否不好?


10

在某些(非常罕见)的情况下,存在以下风险:

  • 重用不打算重用的变量(请参见示例1),

  • 或使用一个变量代替另一个在语义上接近的变量(请参见示例2)。

范例1:

var data = this.InitializeData();
if (this.IsConsistent(data, this.state))
{
    this.ETL.Process(data); // Alters original data in a way it couldn't be used any longer.
}

// ...

foreach (var flow in data.Flows)
{
    // This shouldn't happen: given that ETL possibly altered the contents of `data`, it is
    // not longer reliable to use `data.Flows`.
}

范例2:

var userSettingsFile = SettingsFiles.LoadForUser();
var appSettingsFile = SettingsFiles.LoadForApp();

if (someCondition)
{
    userSettingsFile.Destroy();
}

userSettingsFile.ParseAndApply(); // There is a mistake here: `userSettingsFile` was maybe
                                  // destroyed. It's `appSettingsFile` which should have
                                  // been used instead.

通过引入范围可以减轻这种风险:

范例1:

// There is no `foreach`, `if` or anything like this before `{`.

{
    var data = this.InitializeData();
    if (this.IsConsistent(data, this.state))
    {
        this.ETL.Process(data);
    }
}

// ...

// A few lines later, we can't use `data.Flows`, because it doesn't exist in this scope.

范例2:

{
    var userSettingsFile = SettingsFiles.LoadForUser();

    if (someCondition)
    {
        userSettingsFile.Destroy();
    }
}

{
    var appSettingsFile = SettingsFiles.LoadForApp();

    // `userSettingsFile` is out of scope. There is no risk to use it instead of
    // `appSettingsFile`.
}

看起来不对吗?您会避免使用这种语法吗?初学者很难理解吗?


7
您能否辩称每个独立的“作用域块”应该是其自己的方法或函数?
Dan Pichelman 2013年

我会说“经常”没有很好地实现(但可能不是“设计”问题)。有时是设计使然,这是不可避免的(例如,异常/资源范围界定)。
JustinC 2013年

Answers:


35

如果您的函数太长,以至于您再也无法识别任何不必要的副作用或变量的非法重用,那么现在是时候将其拆分为较小的函数了,这使内部作用域变得毫无意义。

为了得到一些个人经验的支持:几年前,我继承了一个C ++遗留项目,包含约150K行代码,并且其中包含一些正是使用此技术的方法。猜猜看-所有这些方法都太长了。当我们重构大部分代码时,方法变得越来越小,我很确定不再有剩余的“内部作用域”方法。根本不需要它们。


4
但是为什么不好呢?具有许多命名函数也令人困惑。
克里斯·范·贝尔

1
+1确实,如果您需要一个作用域,则可能是您正在执行可以提取到函数中的特定 操作。克里斯(Kris),不是要创建很多函数,而是在发生这些情况(一个块的作用域可能与另一个块的作用域发生冲突)时,通常应该使用一个函数。我也一直在用其他语言(即JavaScript,使用IIFE而不是普通的旧函数)看到这种情况。
本杰明·格林鲍姆

10
@KrisVanBael:“具有许多命名函数也令人困惑”-如果使用描述性名称,则相反。创建太大的函数是代码难以维护甚至难以扩展的最主要原因。
布朗

2
如果存在太多的功能以致于无法明确命名它们,则可能会将其中一些功能汇总为新的类型,因为它们可能与原始功能主体有很大的独立性。有些可能紧密相关,因此可以消除。突然之间,功能就不多了。
JustinC 2013年

3
仍然不服气。通常,长函数确实是不好的。但是说对范围的每种需求都需要单独的功能对我来说太黑了。
克里斯·范·贝尔

3

对于初学者(和任何程序员)而言,考虑以下内容要容易得多:

  • 不使用不相关的变量

而不是想到:

  • 添加旨在防止自己不使用无关紧要的变量的代码结构。

而且,从读者的角度来看,此类块没有明显的作用:删除它们不会对执行产生影响。读者可能会scratch头,试图猜测编码人员想要实现的目标。


2

使用示波器在技术上是正确的。但是,如果要使代码更具可读性,则应使用另一种方法提取该部分,并赋予其含义全名。这样,您可以给出变量的范围,也可以给出任务的名称。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.