web-dev-qa-db-ja.com

条件が真でない場合:デフォルト値またはelse句?

私はプログラマーとStackoverflowを検索しましたが、以前に何度も尋ねられたに違いないと確信しているにもかかわらず、満足のいく答えを見つけることができませんでした。 私が見つけた質問のみ は、読みやすさだけを扱う回答があります。以下のコードの抜粋は、私が書いていた実際のC#コードからの抜粋です。


条件が真であるときに計算された値を持つローカル変数を処理するが、それ以外の場合はデフォルト値を処理する場合は、次のようにします。

最初に値を設定してから、条件が真のときに値を変更しますか?

short version = 1;      // <-- set the value here.
if (separator != -1)
{
    version = Int16.Parse(filename.Substring(separator + 1));
    filename = filename.Substring(0, separator);
}

または、not値を設定し、条件がfalseのときにelseを追加しますか?

short version;
if (separator != -1)
{
    version = Int16.Parse(filename.Substring(separator + 1));
    filename = filename.Substring(0, separator);
}
else
    version = 1;        // <-- set the value here.

どちらかの解決策に直面したときに、プログラマーが誤った期待を抱く可能性がありますか(たとえば、if節が非常に大きく、else節が表示されていない場合)。違いが問題になった経験はありますか?あなたの会社では特定のスタイルが適用されていますか?その理由は?一方が他方よりも好まれる技術的な理由はありますか?最初のものは変数を2回設定しますが、現代の最適化コンパイラでこれが問題になるかどうかはわかりません。

有効な値を割り当てることができるまで、変数はスコープに入らないようにするべきだという考え方がたくさんあります。つまり、変数はneverに誤った値または不要な値が含まれている必要があります。これにより、たとえば、メンテナが後で変数宣言の後、変数が適切に初期化される前に呼び出しを行う可能性を防ぎます。

あなたの短い例では、それらのステートメントがグループであることはかなり明白ですが、時間の経過とともに、関係のないいくつかのステートメントを間に置くことを妨げるものは何もありません。

つまり、あなたの例のbothは間違っています。最初の方法は、デフォルト値を誤って保持する期間が短く、2番目の方法は、値を誤って保持しない期間が短いです。可読性と保守性の観点からの最良のソリューションは次のようなものです。

short version = getVersion(filename, separator);

これには、「関数は正確に1つのことを行う」という考え方に準拠するという追加のボーナスがあります。

12
Karl Bielefeldt

独自のメソッドで機能をカプセル化し、 guard句 を使用して特殊なケースを処理します。

private short getVersion(String filename, int separator)
{
    if (separator == -1)
    {
        return 1;
    }

    return Int16.Parse(filename.Substring(separator + 1));
}

ファイル名の変更は省略しました。これもおそらく独自の方法に値するからです。

private String removeVersion(String filename, int separator)
{
    if (separator == -1)
    {
        return filename;
    }

    return filename.Substring(0, separator);
}

私のC#は錆びていますが、うまくいけば、それが意味をなすのに十分正しいことです。

7

3番目の可能性を省略しました。

short version;
if (separator == -1)
{
    version = 1;        // <-- set the value here.
}
 else
{
    version = Int16.Parse(filename.Substring(separator + 1));
    filename = filename.Substring(0, separator);
}

お気づきのように、コンパイラーは実際にはどちらの方法でもかまいません。私がそれをここに置いた方法、少なくともあなたはあなたが心配していた分離を持っていません。 trueとfalseの間のバージョンの設定は一目で明らかであり、一番上のバージョンは常にナイスでショートであり、それがそれぞれの場合のバージョンの設定を明らかにします-それがあなたが心配しているものである場合。

私は、あなたが(いくつかの投票で締めくくる)本当に好みの問題であることがわかると思います。

2
Matthew Flynn

条件自体に関連する場合は、if-elseブロック内で割り当てを囲むと、より明確になります。条件が失敗した場合に割り当てを無視できることをコンパイラーに示唆するだけでなく、2つのこと(変数の代入と条件)が関連していることをプログラマーに示唆します。同様に、2つが関連していない場合は、それらを分離する(if()ブロックの前または後の割り当て)がおそらく直感的なアプローチになります。もちろん、これはすべてコンテキストに依存し、ケース固有です。しかし、一般的には、関連するものを結び付けて、ロジックで詳しく説明します。

1
zxcdw

マシンに行くとき、どちらのボタンを押すべきですか:コーヒーまたはティー?まあ、それは私が飲みたいものに依存します:コーヒーまたは紅茶... :-)

  • 変数「var」はデフォルトで値「A」を取得する必要があることを表現したい場合は、「B」または「C」の場合もありますが、初期化値を「A」に設定します。

  • コードが「var」をデフォルトなしで特定の値に設定する責任がある場合、私はそれを初期化せずに残します-忘れられたパスがある場合は、Niceコンパイラ警告も表示します。このテクニックを使用したことはありませんが、「final」キーワードを使用すると、変数が(Javaで)1回だけ初期化されるようになります。

しかし、決して省略しないことをお勧めしますが、それは(言語定義により「単一ステートメントになる可能性がある」としても)任意のブロックの周りの{}です。私は、自分や他の人のコードで、「1行になると思った」または「この周りまたは内部に別のif-elseが存在することを知りませんでした」などのことに時間を費やしすぎています。 IDEでサポートされていても、インデントはコンパイラの操作ではありません。ブロックを作成したら、コンパイラに通知してください...さらに2文字入力しても問題はありませんたくさん。

1
Lorand Kedves

より多くのコードを共有してください-適合する場合は完全なクラスです。そうでない場合、きちんとしたアドバイスをする方法はありません。

私はあなたが何をしているのか正確にはわかっていないので、私は推測します。これが1つの方法です...セパレーター変数は何をしますか?理想的には、セパレーターはパラメーターとして渡されますが、セパレーターが-1の場合、最初に呼び出されるべきではないため、意味がありません。

// Initialize some stuff inside of the constructor.

...
private void ParseVersionAndFileName()
{
    if (this.Separator < 0)
    {
        Debug.Assert(separator == -1, "Negative value but not -1? How?");
        return;
    }

    this.Version = Int16.Parse(this.Filename.Substring(this.Separator + 1));
    this.Filename = this.Filename.Substring(0, this.Separator);
}

public string Version
{
    get;
    private set;
}

public int Version
{
    get;
    private set;
}
...
0
Job

ハードウェアアーキテクチャの観点があります。 else句への分岐を初期化して回避することによってコードを記述することにより、分岐予測テーブルを不必要に汚染することを回避できます。比較的些細な例では、何をしても比較と分岐を行う必要があるため、問題にはなりません。

0
Scott Michel