+= new EventHandler(Method) vs += Method [重複].
質問
重複の可能性があります。
C#: ' += anEvent' と ' += new EventHandler(anEvent)' の違い。
イベントを購読するには、2つの基本的な方法があります。
SomeEvent += new EventHandler<ArgType> (MyHandlerMethod);
SomeEvent += MyHandlerMethod;
どのような違いがあるのでしょうか、またどのような場合にどちらを選べばよいのでしょうか。
編集:もし同じなら、なぜVSは長いバージョンをデフォルトにして、コードを乱雑にしているのでしょうか?それは私にはまったく意味がありません。
どのように解決するのですか?
私の最初の回答に対して論争があるようなので、生成されたコードを見ることを含めて、いくつかのテストをしてみることにしました。 と
パフォーマンスを監視することも含めて、いくつかのテストを行うことにしました。まず最初に、これがテストベッドです。デリゲートを持つクラスと、それを消費する別のクラスです。
class EventProducer
{
public void Raise()
{
var handler = EventRaised;
if (handler != null)
handler(this, EventArgs.Empty);
}
public event EventHandler EventRaised;
}
class Counter
{
long count = 0;
EventProducer producer = new EventProducer();
public void Count()
{
producer.EventRaised += CountEvent;
producer.Raise();
producer.EventRaised -= CountEvent;
}
public void CountWithNew()
{
producer.EventRaised += new EventHandler(CountEvent);
producer.Raise();
producer.EventRaised -= new EventHandler(CountEvent);
}
private void CountEvent(object sender, EventArgs e)
{
count++;
}
}
まず最初に、生成されたILを見ます。
.method public hidebysig instance void Count() cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
L_0006: ldarg.0
L_0007: ldftn instance void DelegateTest.Program/Counter::CountEvent(object, class [mscorlib]System.EventArgs)
L_000d: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int)
L_0012: callvirt instance void DelegateTest.Program/EventProducer::add_EventRaised(class [mscorlib]System.EventHandler)
L_0017: ldarg.0
L_0018: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
L_001d: callvirt instance void DelegateTest.Program/EventProducer::Raise()
L_0022: ldarg.0
L_0023: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
L_0028: ldarg.0
L_0029: ldftn instance void DelegateTest.Program/Counter::CountEvent(object, class [mscorlib]System.EventArgs)
L_002f: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int)
L_0034: callvirt instance void DelegateTest.Program/EventProducer::remove_EventRaised(class [mscorlib]System.EventHandler)
L_0039: ret
}
.method public hidebysig instance void CountWithNew() cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
L_0006: ldarg.0
L_0007: ldftn instance void DelegateTest.Program/Counter::CountEvent(object, class [mscorlib]System.EventArgs)
L_000d: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int)
L_0012: callvirt instance void DelegateTest.Program/EventProducer::add_EventRaised(class [mscorlib]System.EventHandler)
L_0017: ldarg.0
L_0018: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
L_001d: callvirt instance void DelegateTest.Program/EventProducer::Raise()
L_0022: ldarg.0
L_0023: ldfld class DelegateTest.Program/EventProducer DelegateTest.Program/Counter::producer
L_0028: ldarg.0
L_0029: ldftn instance void DelegateTest.Program/Counter::CountEvent(object, class [mscorlib]System.EventArgs)
L_002f: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int)
L_0034: callvirt instance void DelegateTest.Program/EventProducer::remove_EventRaised(class [mscorlib]System.EventHandler)
L_0039: ret
}
というわけで、たしかにこれらは同一のILを生成することが判明しました。 私はもともと間違っていたのです。 しかし、それは ではない . このあたり、話がそれてしまうかもしれませんが、イベントやデリゲートについて語るときには、このあたりも含めて考えることが大切なのではないでしょうか。
異なるデリゲートを作成し、比較することは安くはありません。
これを書いたときは、最初の構文でメソッド群をデリゲートとしてキャストできると思っていたのですが、ただの変換であることがわかりました。 しかし、実際に を保存する をデリゲートとして保存する場合は全く異なります。 これをコンシューマーに追加すると
class Counter
{
EventHandler savedEvent;
public Counter()
{
savedEvent = CountEvent;
}
public void CountSaved()
{
producer.EventRaised += savedEvent;
producer.Raise();
producer.EventRaised -= savedEvent;
}
}
これには 非常に 他の 2 つとは性能面で異なる特性を持っていることがわかります。
static void Main(string[] args)
{
const int TestIterations = 10000000;
TimeSpan countTime = TestCounter(c => c.Count());
Console.WriteLine("Count: {0}", countTime);
TimeSpan countWithNewTime = TestCounter(c => c.CountWithNew());
Console.WriteLine("CountWithNew: {0}", countWithNewTime);
TimeSpan countSavedTime = TestCounter(c => c.CountSaved());
Console.WriteLine("CountSaved: {0}", countSavedTime);
Console.ReadLine();
}
static TimeSpan TestCounter(Action<Counter> action, int iterations)
{
var counter = new Counter();
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < TestIterations; i++)
action(counter);
sw.Stop();
return sw.Elapsed;
}
というような結果が一貫して返ってきます。
Count: 00:00:02.4742007
CountWithNew: 00:00:02.4272702
CountSaved: 00:00:01.9810367
それは、ほぼ 20% 保存されたデリゲートを使用する場合と、新しいものを作成する場合の差です。
もちろん、すべてのプログラムがこれほど多くのデリゲートをわずかな時間で追加したり削除したりするわけではありませんが、ライブラリクラスを書いている場合、つまり予測できない方法で使用される可能性があるクラスを書いている場合は、この違いを心に留めておく必要があるでしょう。 を追加し イベントを追加したり削除したりする必要がある場合 (そして、私は個人的に、これを行うコードをたくさん書いてきました)、この違いを心に留めておきたいと思います。
ということで、結論としては
SomeEvent += new EventHandler(NamedMethod)
と書くのと同じことになります。
SomeEvent += NamedMethod
. しかし,もし
を削除します。
そのイベントハンドラを後で
にする必要があります。
を保存します。
デリゲート
. たとえ
Delegate
クラスには、追加したデリゲートとは参照元が異なるデリゲートを削除できる特別なコードがありますが、これを実行するためには自明ではない量の作業を行わなければなりません。
もしデリゲートを保存しないのであれば、それは何の違いもありません - コンパイラはとにかく新しいデリゲートを作成してしまいます。
関連
-
[解決済み】ソケットのアドレス(プロトコル/ネットワークアドレス/ポート)は、通常1つしか使用できない?
-
[解決済み】C# ASP.NET使用時に「WebClientのリクエスト中に例外が発生しました。
-
[解決済み】Sequence contains no matching element(シーケンスにマッチする要素がない
-
[解決済み】バックスラッシュを含むパス文字列のエスケープシーケンスが認識されない件
-
[解決済み】値が期待した範囲に収まらない
-
[解決済み】Entity FrameworkからのSqlException - セッション内で他のスレッドが動作しているため、新しいトランザクションは許可されません。
-
[解決済み] なぜList<T>を継承しないのですか?
-
[解決済み] EqualsメソッドがオーバーライドされたときにGetHashCodeをオーバーライドすることが重要な理由は何ですか?
-
[解決済み] C#で同期メソッドから非同期メソッドを呼び出すには?
-
[解決済み】TをEnumに拘束するGenericメソッドの作成
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】エラー。「戻り値を変更できません」 C#
-
[解決済み】"The ConnectionString property has not been initialized "を修正する方法
-
[解決済み】クロススレッド操作が有効でない。作成されたスレッド以外のスレッドからアクセスされたコントロール
-
[解決済み] [Solved] 不正な文字列値: '\xEFxBFxBD' for column
-
[解決済み】Unity 「関連するスクリプトを読み込むことができません」「Win32Exception: システムは指定されたファイルを見つけることができません"
-
[解決済み】Linq 構文 - 複数列の選択
-
[解決済み】2年前のMSDateを把握する【クローズド
-
[解決済み】インデックスが範囲外でした。コレクションパラメータname:indexのサイズより小さく、非負でなければなりません。
-
[解決済み】画像のペイントにTextureBrushを使用する方法
-
C#: += anEvent' と ' += new EventHandler(anEvent)' の違いについて