52

C# の詳細(これまでのところ優れた本) で、Skeet はevents are not fields を説明しています。私はこのセクションを何度も読みましたが、なぜ違いが生じるのか理解できません。

私は、イベントとデリゲート インスタンスを混同している開発者の 1 人です。私の考えでは、それらは同じです。どちらも単なる間接的な形ではありませんか? 両方をマルチキャストできます。イベントは省略形としてフィールドとして設定されます...確かに。ただし、ハンドラーを追加または削除しています。イベントが発生したときに呼び出されるようにそれらを積み重ねます。デリゲートでも同じことをして、積み上げて呼び出してはいけませんか?

4

5 に答える 5

47

他の答えは基本的に正しいですが、別の見方をすると次のようになります。

私は、イベントとデリゲート インスタンスを混同している開発者の 1 人です。私の考えでは、それらは同じです。

木を見て森を見ずという古いことわざが思い浮かびます。私が行う違いは、イベントがデリゲート インスタンスのフィールドよりも高い "セマンティック レベル" にあるということです。イベントは、消費者に「こんにちは、私は何かが起こったときにあなたに話すのが好きなタイプです」と伝えます。タイプはイベントをソースします。それは公共契約の一部です。

実装の詳細として、そのクラスが、誰がそのイベントをリッスンすることに興味を持っているかを追跡することをどのように選択するか、およびイベントが発生していることをサブスクライバーにいつ、何を伝えるかは、クラスの仕事です。通常、マルチキャスト デリゲートでそうするのがたまたまですが、それは実装の詳細です。これは非常に一般的な実装の詳細であるため、この 2 つを混同するのは理にかなっていますが、実際には 2 つの異なるものがあります。パブリック サーフェスとプライベート実装の詳細です。

同様に、プロパティはオブジェクトのセマンティクスを記述します。顧客には名前があるため、Customer クラスには Name プロパティがあります。「彼らの名前」は顧客の所有物であると言うかもしれませんが、「彼らの名前」が顧客の分野であるとは決して言いません。これは特定のクラスの実装の詳細であり、ビジネス セマンティクスに関する事実ではありません。通常、プロパティがフィールドとして実装されることは、クラスのメカニズムのプライベートな詳細です。

于 2012-04-29T00:09:14.243 に答える
45

プロパティもフィールドではありませんが、そのように感じます。これらは実際には、特殊な構文を持つ 1 対のメソッド (getter と setter) です。

同様に、イベントは特別な構文を持つ 1 対のメソッド (subscribe と unsubscribe) です。

どちらの場合も、通常、クラス内にプライベートな「バッキング フィールド」があり、getter/setter/subscribe/unsubscribe メソッドによって操作される値を保持します。また、プロパティとイベントの両方に自動実装された構文があり、コンパイラがバッキング フィールドとアクセサー メソッドを生成します。

目的も同じです。プロパティは、新しい値を格納する前にいくつかの検証ロジックが実行されるフィールドへの制限付きアクセスを提供します。また、イベントはデリゲート フィールドへの制限付きアクセスを提供します。このフィールドでは、コンシューマーはサブスクライブまたはサブスクライブ解除のみが可能であり、サブスクライバーのリストを読み取ったり、リスト全体を一度に置き換えたりすることはできません。

于 2012-04-28T20:12:57.433 に答える
26

イベントを宣言する 2 つの方法を考えてみましょう。

明示的なadd/removeメソッドを使用してイベントを宣言するか、そのようなメソッドを使用せずにイベントを宣言します。

つまり、次のようにイベントを宣言します。

public event EventHandlerType EventName
{
    add
    {
        // some code here
    }
    remove
    {
        // some code here
    }
}

または、次のように宣言します。

public event EventHandlerType EventName;

問題は、いくつかの点では同じものであり、別の点では完全に異なるということです。

外部コード、つまり、イベントを発行するクラスの外部のコードの観点からは、それらはまったく同じものです。イベントをサブスクライブするには、メソッドを呼び出します。購読を解除するには、別のメソッドを呼び出します。

違いは、上記の 2 番目のコード例では、これらのメソッドがコンパイラによって提供されることですが、それは依然としてそうなる方法です。イベントをサブスクライブするには、メソッドを呼び出します。

ただし、C# でこれを行うための構文は同じで、次のいずれかを行います。

objectInstance.EventName += ...;

また:

objectInstance.EventName -= ...;

したがって、「外部の視点」から見ると、2 つの方法にまったく違いはありません。

ただし、クラス内では違いがあります。

クラス内のEventName識別子にアクセスしようとすると、実際にfieldはプロパティをサポートするを参照していますが、これは/メソッドを明示的に宣言しない構文を使用している場合に限られますaddremove

典型的なパターンは次のようなものです。

public event EventHandlerType EventName;

protected void OnEventName()
{
    var evt = EventName;
    if (evt != null)
        evt(this, EventArgs.Empty);
}

この場合、 を参照しているとき、EventName実際には 型のデリゲートEventHandlerTypeを保持するフィールドを参照しています。

addただし、 /removeメソッドを明示的に宣言した場合EventName、クラス内で識別子を参照することは、クラス外と同じようになります。コンパイラは、サブスクリプションを格納するフィールドまたはその他のメカニズムを知っていることを保証できないためです。 .

于 2012-04-28T20:25:23.340 に答える
10

イベントは、デリゲートのアクセサーです。プロパティがフィールドのアクセサーであるように。まったく同じユーティリティを使用して、コードがデリゲート オブジェクトをいじるのを防ぎます。プロパティに get および set アクセサーがあるように、イベントには add および remove アクセサーがあります。

add および remove アクセサーを自分で記述しないと、コンパイラーがそれらを自動生成するため、プロパティとは多少異なる動作をします。デリゲート オブジェクトを格納するプライベート バッキング フィールドを含めます。自動プロパティに似ています。

これは頻繁に行うことではありませんが、決して珍しいことではありません。たとえば、Winforms コントロールのイベントはEventHandlerListに格納され、追加/削除アクセサーはその AddHandler() および RemoveHandler() メソッドを通じてそのリストを操作します。すべてのイベント (多数あります) がクラス内の 1 つのフィールドのみを必要とするという利点があります。

于 2012-04-28T20:25:48.007 に答える
0

デリゲートは名前空間スコープ内 (クラス外) で宣言でき、イベントはクラス内でのみ宣言できるという以前の回答に追加できます。これはデリゲートがクラスだからです!

もう 1 つの違いは、イベントの場合、それを起動できるのはそれを含むクラスだけです。含まれているクラスを介してサブスクライブ/サブスクライブ解除できますが、それを起動することはできません (デリゲートとは対照的に)。ということで、慣例でprotected virtual OnSomething(object sender, EventArgs e). 子孫が発火の実装をオーバーライドできるようにするためです。

于 2012-05-02T10:14:05.810 に答える