web-dev-qa-db-ja.com

配列要素の存在を「無効にする」ためのnull条件演算子

新しいC#6.0 null条件演算子は、より簡潔で複雑なコードを記述するための便利な手段です。顧客の配列があると仮定すると、customersがこれを使用してnullの場合、長さではなくnullを取得できます(例 [〜#〜] msdn [〜#〜] ) :

int? length = customers?.Length;

同様に、次のようにして、顧客の代わりにnullを取得できます。

Customer first = customers?[0];

さらに複雑な式の場合、customersがnull、最初の顧客がnull、または最初の顧客のOrdersオブジェクトがnullの場合、nullが生成されます。

int? count = customers?[0]?.Orders?.Count();

しかし、存在しない顧客の興味深いケースがあり、ヌル条件演算子が対応していないようです。上記で、nullの顧客が対象であることがわかりました。つまり、customers配列のエントリがnullの場合です。しかし、それは存在しない顧客とはまったく異なります。顧客を探しています5 3要素の配列または顧客n(0要素のリスト)。 (同じ議論がディクショナリの検索にも適用されることに注意してください。)

Null条件演算子は、NullReferenceExceptionの影響を否定することに専念しているように思えます。 IndexOutOfRangeExceptionまたはKeyNotFoundExceptionは単独で公開されており、隅々まで行き渡っていて、自分で対処する必要があります。私は、null条件演算子の精神に基づいて、これらのケースも処理できるはずであると提出します...これが私の質問につながります。

見逃しましたか?ヌル条件は、この式を本当にカバーするためのエレガントな方法を提供しますか...

customers?[0]?.Orders?.Count();

...ゼロ番目の要素がないときは?

20
Michael Sorens

いいえ、それはnull-条件付き演算子であり、indexoutofrange-条件付き演算子ではなく、次のような構文糖子にすぎないためです。

int? count = customers?[0]?.Orders?.Count();

if (customers != null && customers[0] != null && customers[0].Orders != null)
{
    int count = customers[0].Orders.Count();
}

0番目の顧客がいない場合は、通常のIndexOutOfRangeExceptionを取得できることがわかります。

これを回避する方法の1つは、インデックスを確認し、存在しない場合はnullを返す拡張メソッドを用意することです。

public static Customer? GetCustomer(this List<Customer> customers, int index)
{
    return customers.ElementAtOrDefault(index); // using System.Linq
}

次に、あなたのチェックは次のようになります:

int? count = customers?.GetCustomer(0)?.Orders?.Count();
26
Steve
customers?.FirstOrDefault()?.Orders?.Count();

ゼロ、問題ありません。

8
Scott Hannen

インデックス作成の安全性はサポートされていません。なぜなら、それを理解すると、インデクサーは実際には他のタイプのメソッドの単なるシンタックスシュガーに過ぎないからです。

例えば:

public class MyBadArray
{
    public Customer this[int a]
    {
        get
        {
            throw new OutOfMemoryException();
        }
    }
}

var customers = new MyBadArray(); 
int? count = customers?[5]?.Orders?.Count();

これはここで捕まえる必要がありますか? KeyNotFoundExceptionと同様に、例外がより賢明であるが、実装しているコレクションのタイプに固有である場合はどうなりますか?継続的に?.機能を更新していく必要があります。

さらに、?.は例外をキャッチしません。それはそれらを防ぎます。

var customer = customers?[5];は、実際には次のようにコンパイルされます。

Customer customer = null;
if (customers != null)
    customer = customers[5];

例外をキャッチすることは非常に難しくなります。例えば:

void Main()
{
    var thing = new MyBadThing(); 
    thing.GetBoss()?.FireSomeone();
}

public class MyBadThing
{
    public class Boss
    {
        public void FireSomeone() 
        { 
            throw new NullReferenceException();
        }
    }
    public Boss GetBoss()
    {
        return new Boss();
    }
}

単に例外をキャッチしている場合は、次のように記述されます。

Boss boss = customer.GetBoss();
try 
{
    boss.FireSomeone();
} catch (NullReferenceException ex) { 

}

ボスがnullの場合にスローされるnull参照例外ではなく、FireSomeone内の例外を実際にキャッチします。

インデックスルックアップの例外、キーが見つからないという例外などをキャッチする場合にも、同様の悪い問題が発生します。

3
Rob