関数内での内部スコープブロックの使用は悪いスタイルですか?


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年

回答:


35

関数が長すぎて不要な副作用や変数の不正な再利用を認識できない場合は、関数を小さな関数に分割して、内部スコープを無意味にします。

これを個人的な経験で裏付けるために、私は数年前に約150K行のコードを含むC ++レガシープロジェクトを継承しました。このプロジェクトには、まさにこの手法を使用するいくつかのメソッドが含まれていました。そして、何だと思います-これらの方法はすべて長すぎました。ほとんどのコードをリファクタリングしたので、メソッドはどんどん小さくなり、「内部スコープ」メソッドが残っていないことはほぼ間違いありません。それらは単に必要ではありません。


4
しかし、なぜそれが悪いのですか?多くの名前付き関数があることも混乱を招きます。
Kris Van Bael 2013年

1
+1正確に言うと、スコープが必要な場合は、関数に抽出できる特定の アクションを実行している可能性があります。クリス、それはたくさんの関数を作成することではなく、これらのケースが発生した場合(あるブロックのスコープが別のブロックと競合する可能性がある場合)、通常は関数が使用されるべきでした。他の言語(つまり、JavaScriptで、プレーンな古い関数の代わりにIIFEが使用されている)でも、これは常に見られます。
Benjamin Gruenbaum 2013年

10
@KrisVanBael:「多くの名前付き関数を使用することも混乱を招く」-説明的な名前を使用すると、まったく逆のことが言えます。大きすぎる関数を作成することは、コードの保守が難しくなり、拡張がさらに困難になる最大の理由です。
ドクブラウン

2
関数の数が多すぎて、それらの名前を明確に付けることが問題になる場合は、元の関数本体から大幅に独立している可能性があるため、一部の関数を新しいタイプにまとめることができます。一部は非常に密接に関連しているため、排除できる可能性があります。突然、それほど多くの機能がなくなります。
JustinC 2013年

3
まだ納得できません。多くの場合、長い関数は実際に悪いです。しかし、スコープの必要性ごとに個別の機能が必要であると言うのは、私には白黒すぎます。
Kris Van Bael 2013年

3

初心者(およびプログラマー)が考えることははるかに簡単です。

  • 関連しない変数を使用しない

考えるより:

  • 関連しない変数を使用しないようにするためのコード構造を追加する。

さらに、読者の観点から見ると、そのようなブロックには明らかな役割はありません。それらを削除しても、実行に影響はありません。読者は、コーダーが達成したかったことを推測しようとして頭を掻きます。


2

スコープの使用は技術的に正しいです。ただし、コードを読みやすくするには、その部分を別のメソッドで抽出して、意味のある完全な名前を付ける必要があります。このようにして、変数のスコープを指定し、タスクの名前を指定することもできます。


0

変数を再利用することはコードのにおいよりも多いことに同意します。おそらく、そのようなコードは、より小さな自己完結型のブロックにリファクタリングする必要があります。

それらがポップアップする傾向があるときC#で1つの特定のシナリオ、OTOHがあります-それはswitch-case構造を使用するときです。状況は非常にうまく説明されています:中括弧の有無にかかわらずC#のSwitchステートメント…違いは何ですか?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.