web-dev-qa-db-ja.com

C#でnullでない場合のメソッド呼び出し

この声明を何らかの形で短くすることは可能ですか?

if (obj != null)
    obj.SomeMethod();

私はたまたまこれを書くので、かなり面倒ですから。私が考えることができる唯一のものはNull Objectパターンを実装することですが、それは私が毎回できることではなく、確かに構文を短縮する解決策ではありません。

イベントに関する同様の問題、ここで

public event Func<string> MyEvent;

そして次に呼び出す

if (MyEvent != null)
    MyEvent.Invoke();
81
Jakub Arnold

C#6以降では、次のものを使用できます。

MyEvent?.Invoke();

または:

obj?.SomeMethod();

?.はヌル伝搬演算子であり、オペランドがnullの場合に.Invoke()を短絡させます。オペランドは1回しかアクセスされないため、「チェックと呼び出しの間で値が変更される」問題のリスクはありません。

===

C#6よりも前:いいえ:1つの例外を除き、nullセーフマジックはありません。拡張メソッド-例:

public static void SafeInvoke(this Action action) {
    if(action != null) action();
}

これは有効です:

Action act = null;
act.SafeInvoke(); // does nothing
act = delegate {Console.WriteLine("hi");}
act.SafeInvoke(); // writes "hi"

イベントの場合、これには競合状態も除去できるという利点があります。つまり、一時変数は必要ありません。したがって、通常は次のものが必要です。

var handler = SomeEvent;
if(handler != null) handler(this, EventArgs.Empty);

しかし:

public static void SafeInvoke(this EventHandler handler, object sender) {
    if(handler != null) handler(sender, EventArgs.Empty);
}

単純に使用できます:

SomeEvent.SafeInvoke(this); // no race condition, no null risk
137
Marc Gravell

あなたが探しているのは Null Conditional ( "coalescing"ではない)演算子です:?.。 C#6以降で利用可能です。

あなたの例はobj?.SomeMethod();です。 objがnullの場合、何も起こりません。メソッドに引数がある場合、たとえばobj?.SomeMethod(new Foo(), GetBar());objがnullの場合、引数は評価されません。これは、引数の評価に副作用がある場合に重要です。

そして、連鎖が可能です:myObject?.Items?[0]?.DoSomething()

26
Vimes

簡単な拡張方法:

    public static void IfNotNull<T>(this T obj, Action<T> action, Action actionIfNull = null) where T : class {
        if(obj != null) {
            action(obj);
        } else if ( actionIfNull != null ) {
            actionIfNull();
        }
    }

例:

  string str = null;
  str.IfNotNull(s => Console.Write(s.Length));
  str.IfNotNull(s => Console.Write(s.Length), () => Console.Write("null"));

または、代わりに:

    public static TR IfNotNull<T, TR>(this T obj, Func<T, TR> func, Func<TR> ifNull = null) where T : class {
        return obj != null ? func(obj) : (ifNull != null ? ifNull() : default(TR));
    }

例:

    string str = null;
    Console.Write(str.IfNotNull(s => s.Length.ToString());
    Console.Write(str.IfNotNull(s => s.Length.ToString(), () =>  "null"));
10
katbyte

はい、C#6.0で- https://msdn.Microsoft.com/en-us/magazine/dn802602.aspx

object?.SomeMethod()
4
epylar

イベントは、削除されない空のデフォルトデリゲートで初期化できます。

public event EventHandler MyEvent = delegate { };

Nullチェックは必要ありません。

[更新、これを指摘してくれたBevanに感謝]

ただし、パフォーマンスへの影響の可能性に注意してください。私が行った簡単なマイクロベンチマークは、「デフォルトデリゲート」パターンを使用すると、サブスクライバーのいないイベントの処理が2〜3倍遅くなることを示しています。 (私のデュアルコア2.5GHzラップトップでは、279msを意味します:購読されていないイベントを5,000万回発生させるのに785msです。)アプリケーションのホットスポットについては、それが考慮すべき問題になる可能性があります。

4
Sven Künzler

提案されているような拡張方法の制限は、実際には競合状態の問題を解決するのではなく、それらを非表示にします。

public static void SafeInvoke(this EventHandler handler, object sender)
{
    if (handler != null) handler(sender, EventArgs.Empty);
}

前述のように、このコードは一時変数を使用したソリューションと同等のエレガントなものですが、...

両方の問題イベントのサブスクライブは、イベントからサブスクライブされていない後に呼び出される可能性があります。これは、デリゲートインスタンスが一時変数にコピーされた後(または上記のメソッドでパラメーターとして渡された後)、デリゲートが呼び出される前に登録解除が発生する可能性があるためです。

一般に、このような場合、クライアントコードの動作は予測できません。コンポーネントの状態では、イベント通知を既に処理できませんでした。クライアントコードを処理する方法で記述することは可能ですが、クライアントに不必要な責任を負わせることになります。

スレッドの安全性を確保する唯一の既知の方法は、イベントの送信者にロックステートメントを使用することです。これにより、すべてのsubscriptions\unsubscriptions\invocationが確実にシリアル化されます。

より正確にするには、add\removeイベントアクセサメソッドで使用される同じ同期オブジェクト(デフォルトは 'this')にロックを適用する必要があります。

2
andrey.tsykunov

ケニー・エリアソンの答えに同意します。拡張メソッドを使用します。拡張メソッドと必要なIfNotNullメソッドの概要を以下に示します。

拡張メソッド(IfNotNullメソッド)

2
Rohit

This Ian Griffithsの記事は、使用すべきでない巧妙なトリックであると彼が結論付けた問題に対する2つの異なるソリューションを提供します。

2
Darrel Miller

良くないかもしれませんが、私の意見では、より読みやすいのは、拡張メソッドを作成することです

public static bool IsNull(this object obj) {
 return obj == null;
}
0
Kenny Eliasson