web-dev-qa-db-ja.com

C#タイプのパターンマッチングで、従来のスイッチブロックとは異なる変数スコープ動作が使用されるのはなぜですか?

従来のスイッチブロックにはスコープが1つあるため、次の場合はコンパイラエラー「 'message'という名前のローカル変数または関数がすでにこのスコープで定義されています」をスローします。

switch(value)
{
    case 1:
        string message = "Val: 1";
        break;
    case 2:

        string message = "Val: 2";
        break;
}

Eric Lippertによると

合理的な質問は、「なぜこれが合法ではないのか」ということです。理にかなった答えは「まあ、なぜそうあるべきなのか」です。 2つの方法のいずれかを使用できます。これは合法です:

switch(y)
{
    case 1:  int x = 123; ... break;
    case 2:  int x = 456; ... break;
}

またはこれは合法です:

switch(y)
{
    case 1:  int x = 123; ... break;
    case 2:  x = 456; ... break;
}

しかし、両方の方法を持つことはできません。 C#の設計者は、より自然な方法であると思われる2番目の方法を選択しました。

他にも良い説明があります これのように

他のすべてのケースでは、「通常の」ローカル変数のスコープが中括弧({})で区切られたブロックであることは、もっともな理由だと思います。

では、なぜ型パターンマッチングスイッチブロックではスコープの動作が異なるのでしょうか。

Animal p = new Dog();

switch(p)
{
    case Dog a:
        break;
    case Cat a: // Why is this legal?           
        break;
}
2
rory.ap

短い答えは、aはパターン変数であり、パターン変数のスコープはそれらを含むブロックにあるためです。

たとえば、そのswitchif (p is Dog a) return;で続行すると、2つのa変数がすでに定義されているというメッセージが表示されるため、コンパイルされなくなります。これは、ifの「包含ブロック」がifを含むブロックであるためです。ただし、caseラベルのパターン変数の場合、包含ブロックはcaseブロックです。したがって、あなたの例では、これらの2つのa変数は別々のブロックに存在します。

詳細については、C#7ドキュメントの パターン変数のスコープ を参照してください。

スイッチの変数スコープがこのように変更された理由を理解するには、次のコードを検討してください。

switch(animal)
{
    case Dog dog1 when dog1.AverageWeightKg > 20:
        // do something with dog1
        break;
    case Dog dog2 when dog2.AverageWeightKg > 10:
        // do something with dog2
        break;
    case Dog dog3:
        // do something with dog3
        break;
    case Cat cat:            
        // do something with cat
        break;
}

それらdog1dog2およびdog3変数名は本当に醜いです。したがって、C#で変数スコープの他の側面との不整合が生じた一方で、パターン変数のスコープ規則を変更することが決定されました。つまり、上記のコードをはるかにエレガントな方法で記述できます。

switch(animal)
{
    case Dog dog when dog.AverageWeightKg > 20:
        // do something with dog
        break;
    case Dog dog when dog.AverageWeightKg > 10:
        // do something with dog
        break;
    case Dog dog:
        // do something with dog
        break;
    case Cat cat:            
        // do something with cat
        break;
}
4
David Arno