2017-03-27 23 views
0

Iveは今のところコーディングをしていて面白いことに気づいただけです。だから、通常、私はクラスでイベントを実施していたとき、私はそれを呼び出すようにしようとする前に、イベントのNULLステータスを確認したいので、私は、次の構文を使用したい:クイックC#シンタックスの説明

private void OnEvent(EventArgs e) 
{ 
    if (Event != null) Event(this, e); 
} 

をしかし、私はビジュアルで、今日何かに気づきましたStudioは、それそれのコードの簡素化の提案、それは単純化として、次の構文を提案した:

private void OnEvent(EventArgs e) 
{ 
    Event?.Invoke(this, e); 
} 

この精通誰です「?」構文?それは何か、あるいは単に代議員のヌル状態をチェックするための一般的な省略形ですか? Linqフレームワークの一部ではなく、その構文に組み込まれています。これに関する何らかの洞察とそのために使われていることが役に立つでしょう。私はかなりの検索を行っていませんが、具体的に何かを見つけることはできません。

+1

これは「null条件付き演算子」と呼ばれ、C#6.0の一部です。https://msdn.microsoft.com/en-us/library/dn986595.aspx –

+0

これは[Null条件付き演算子](https ://msdn.microsoft.com/en-us/library/dn986595.aspx)。 – Igor

+0

最初の例はスレッドセーフではないので、より良いことに注意してください。 ifの後、しかしあなたの呼び出しの前に、別のスレッドが 'Event'をnullに設定するとどうなりますか?あなたがそれを使いたくない場合、正しい構文は 'var localEvent = Event; if(localEvent!= null)localEvent(this、e); 'これはスレッドの安全性を保証するためです。 – dman2306

答えて

0

これはヌル条件演算子と呼ばれ、状況の束で有用です。それとNull-Coalescing演算子(??)は、何かを逆参照する前にヌルをチェックするか、クリーンなデフォルトを提供する、本当にきれいで表現力豊かな方法です。詳細情報については

https://msdn.microsoft.com/en-CA/library/dn986595.aspx

6

は、私は上記のいくつかのコメントを残したが、私は、彼らが答えに出呼ばれるほど重要であると感じています。

まず、他の人が指摘したように、これは空の条件付き演算子です。

別のスレッドに変更した場合、イベントがチェックした後にヌルになる可能性があるため、このコードのバージョンは貧弱なスタイルと見なされます。

private void OnEvent(EventArgs e) 
{ 
    if (Event != null) Event(this, e); 
} 

は、しかし、私が呼び出すマルチスレッドプログラムは、上のイベントをサブスクライブし、退会ことに注意してください任意のスレッドは比較的まれです。

ヌル条件演算子の前に、標準的なアドバイスが追加されましたがclumsyを書くことだった:

private void OnEvent(EventArgs e) 
{ 
    var ev = Event; 
    if (ev != null) ev(this, e); 
} 

レースがある場合、これはnullの間接参照を排除します。

ヌル条件演算子は同じです。

private void OnEvent(EventArgs e) 
{ 
    Event?.Invoke(this, e); 
} 

これはvar ev = Event; if (ev != null) ev.Invoke(this, e);のためだけの省略形です。

ローカルで、またはより簡潔には、?.で、後者の2つのバージョンが「スレッドセーフ」であることを伝えます。 これらはではありません。これらはヌルを逆参照しませんが、それは面白いレースではありません。

Thread Alpha: create disposable object X -- say, a log file writer 
Thread Alpha: subscribe X.H to event 
Thread Bravo: Cause event to fire 
Thread Bravo: Save X.H into local and check for null. It's not null. 
Thread Alpha: Unsubscribe X.H from event 
Thread Alpha: Call Dispose on X -- the log file is now closed. 
Thread Bravo: Invoke X.H via the local 

そして今、我々は、オブジェクトが正しく実装されている場合、例外「に配置されたオブジェクト」をスローする必要があり、破棄されたオブジェクト、上のメソッドを呼び出しています:面白いレースがあります。もしそうでなければ、ちょっと、クローズドファイルに書き込もうとすると、すべてが壊れてしまいます。マルチスレッド環境で

、イベントハンドラは、それらがイベントから退会されていても後、永遠にをを呼び出しても安全であることをを必要としています。コールサイトの「スレッドの安全性」はその問題を解決するものではありません。これはハンドラの要件です。しかし、ハンドブックの著者は、マルチスレッド環境で使用されることをどのようにして知っているのですか?しばしば、彼らはしません。だから事は壊れます。

好きではない場合は、は、複数のスレッドでイベントを使用するプログラムを作成しないでください。正しいことを得るのは本当に難しく、誰もが - ソースとハンドラの両方が協力して動作することを確認する必要があります。

ここで、ハンドラにロックを設定する解決策ではないと言う人もいますが、

private object HandleLocker = new object(); 
... 
private void OnEvent(EventArgs e) 
{ 
    lock (HandleLocker) 
    { 
    if (Event != null) 
     Event(this, e); 
    } 
} 

そして同様にaddEventremoveにロックを追加します。

これは病気よりも悪化する治療法です。

Thread Bravo takes out a lock on some object Foo and obtains it. 
Thread Charlie wishes to fire the event. 
Thread Charlie takes out a lock on HandleLocker and obtains it. 
Thread Charlie calls the handler. The handler blocks trying to obtain Foo. 
Thread Bravo attempts to subscribe a new handler, and blocks on HandleLocker. 

ここでは、2つのスレッドのそれぞれがもう一方のスレッドを完了するのを待っています。 BravoはHandleLockerがCharlieによってリリースされるまで動かすことができず、CharlieはBravoによってFooがリリースされるまで動かすことができません。

これを絶対にしないでください。再度:マルチスレッドのイベント処理プログラムでは、が失効している間にハンドラが呼び出されることを保証する必要があります。ロックを使用してを実行することはできません。これは悪い状況です。ロックはスレッドを管理するための標準的なメカニズムです。

マルチスレッドは難しいです。事が間違っている可能性があるすべての方法とそれらを回避する方法を理解していない場合は、マルチスレッドプログラムを記述しないでください。