Ist die Verwendung von internen Bereichsblöcken innerhalb einer Funktion ein schlechter Stil?

10

Es gibt einige (recht seltene) Fälle, in denen das Risiko besteht, dass:

  • Wiederverwendung einer Variablen, die nicht wiederverwendet werden soll (siehe Beispiel 1),

  • oder Verwenden einer Variablen anstelle einer anderen, semantisch nah (siehe Beispiel 2).

Beispiel 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`.
}

Beispiel 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.

Dieses Risiko kann durch die Einführung eines Anwendungsbereichs gemindert werden:

Beispiel 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.

Beispiel 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`.
}

Sieht es falsch aus Würden Sie eine solche Syntax vermeiden? Ist es für Anfänger schwer zu verstehen?

Arseni Mourzenko
quelle
7
Könnten Sie argumentieren, dass jeder unabhängige "Scope Block" stattdessen eine eigene Methode oder Funktion sein sollte?
Dan Pichelman
Ich würde sagen "oft" nicht so gut implementiert wie es sein könnte (aber wahrscheinlich kein "Design" -Problem). Manchmal ist dies beabsichtigt (z. B. Ausnahme- / Ressourcenbereich).
JustinC

Antworten:

34

Wenn Ihre Funktion so lang ist, dass Sie keine unerwünschten Nebenwirkungen oder illegalen Wiederverwendungen von Variablen mehr erkennen können, ist es an der Zeit, sie in kleinere Funktionen aufzuteilen - was einen internen Bereich sinnlos macht.

Um dies durch einige persönliche Erfahrungen zu belegen: Vor einigen Jahren habe ich ein C ++ - Legacy-Projekt mit ~ 150.000 Codezeilen geerbt, das einige Methoden enthielt, die genau diese Technik verwendeten. Und raten Sie mal - all diese Methoden waren zu lang. Da wir den größten Teil dieses Codes überarbeitet haben, wurden die Methoden immer kleiner, und ich bin mir ziemlich sicher, dass es keine verbleibenden "internen Bereichs" -Methoden mehr gibt. Sie werden einfach nicht benötigt.

Doc Brown
quelle
4
Aber warum ist es schlecht? Es ist auch verwirrend, viele benannte Funktionen zu haben.
Kris Van Bael
1
+1 Wenn Sie einen Bereich benötigen, führen Sie wahrscheinlich eine bestimmte Aktion aus , die in eine Funktion extrahiert werden kann. Kris, es geht nicht darum, viele, viele Funktionen zu erstellen. In diesen Fällen (in denen der Umfang eines Blocks mit dem eines anderen in Konflikt stehen könnte) sollte normalerweise eine Funktion verwendet werden. Ich sehe dies die ganze Zeit auch in anderen Sprachen (nämlich JavaScript mit IIFE anstelle von einfachen alten Funktionen).
Benjamin Gruenbaum
10
@KrisVanBael: "Viele benannte Funktionen zu haben ist auch verwirrend" - wenn Sie beschreibende Namen verwenden, ist das Gegenteil der Fall. Das Erstellen zu großer Funktionen ist der Hauptgrund dafür, dass Code schwer zu warten und noch schwieriger zu erweitern ist.
Doc Brown
2
Wenn es so viele Funktionen gibt, dass die eindeutige Benennung zu einem Problem wird, können möglicherweise einige von ihnen zu einem neuen Typ zusammengefasst werden, da sie möglicherweise erheblich von ihrem ursprünglichen Funktionskörper unabhängig sind. Einige könnten so eng miteinander verwandt sein, dass sie beseitigt werden könnten. Plötzlich gibt es nicht mehr so ​​viele Funktionen.
JustinC
3
Immer noch nicht überzeugt. Oft sind lange Funktionen in der Tat schlecht. Aber zu sagen, dass jedes Bedürfnis nach Umfang eine separate Funktion erfordert, ist mir zu schwarz-weiß.
Kris Van Bael
3

Für Anfänger (und jeden Programmierer) ist es viel einfacher, an Folgendes zu denken:

  • keine Variable verwenden, die nicht relevant ist

als zu denken:

  • Hinzufügen von Codestrukturen, um zu verhindern, dass er keine nicht relevante Variable verwendet.

Darüber hinaus spielen solche Blöcke aus Lesersicht keine offensichtliche Rolle: Das Entfernen hat keine Auswirkungen auf die Ausführung. Der Leser kann sich am Kopf kratzen und versuchen zu erraten, was der Codierer erreichen wollte.

Mouviciel
quelle
2

Die Verwendung eines Oszilloskops ist technisch richtig. Wenn Sie Ihren Code jedoch lesbarer machen möchten, sollten Sie diesen Teil in einer anderen Methode extrahieren und ihm einen vollständigen Namen geben. Auf diese Weise können Sie einen Bereich Ihrer Variablen und auch einen Namen für Ihre Aufgabe angeben.

DeveloperArnab
quelle
0

Ich bin damit einverstanden, dass die Wiederverwendung von Variablen häufig ein Codegeruch ist. Wahrscheinlich sollte ein solcher Code in kleineren, in sich geschlossenen Blöcken überarbeitet werden.

Es gibt ein bestimmtes Szenario, OTOH, in C #, in dem sie zum Popup neigen - nämlich bei Verwendung von Switch-Case-Konstrukten. Die Situation wird sehr gut erklärt in: C # Switch-Anweisung mit / ohne geschweifte Klammern… was ist der Unterschied?

Dejan Stanič
quelle