[解決済み] .NETのイベント署名 -- Strong Typedの'Sender'を使用?[クローズド]
質問
私が提案していることは、.NETのガイドラインに従っていないこと、したがって、この理由だけではおそらく良くない考えであることは十分に理解しています。しかし、私は2つの可能な観点からこれを検討したいと思います。
(1) 100%内部目的である私自身の開発作業にこれを使用することを考慮すべきかどうか。
(2) これはフレームワークの設計者が変更または更新を検討できる概念ですか?
私は、現在の.NETデザインパターンである「オブジェクト」としてタイプする代わりに、強いタイプされた「送信者」を利用するイベント署名を使用することについて考えています。すなわち、次のような標準的なイベント署名を使用する代わりにです。
class Publisher
{
public event EventHandler<PublisherEventArgs> SomeEvent;
}
以下のように、強い型の'sender'パラメータを利用したイベントシグネチャを使用することを検討しています。
まず、"StrongTypedEventHandler"を定義します。
[SerializableAttribute]
public delegate void StrongTypedEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
これは、Action<TSender, TEventArgs>と大差ないのですが、このAction<TSender, TEventArgs>を利用することで
StrongTypedEventHandler
を使用することで、TEventArgsが次のような派生をすることを強制しています。
System.EventArgs
.
次に、例としてパブリッシングクラスでStrongTypedEventHandlerを以下のように利用することができます。
class Publisher
{
public event StrongTypedEventHandler<Publisher, PublisherEventArgs> SomeEvent;
protected void OnSomeEvent()
{
if (SomeEvent != null)
{
SomeEvent(this, new PublisherEventArgs(...));
}
}
}
上記の配置により、サブスクライバはキャスティングを必要としない強い型のイベントハンドラを利用することができるようになります。
class Subscriber
{
void SomeEventHandler(Publisher sender, PublisherEventArgs e)
{
if (sender.Name == "John Smith")
{
// ...
}
}
}
私は、これが標準的な.NETのイベント処理パターンを壊していることを十分に理解しています。しかし、contravarianceによって、サブスクライバが希望すれば、従来のイベント処理署名を使用できることを心に留めておいてください。
class Subscriber
{
void SomeEventHandler(object sender, PublisherEventArgs e)
{
if (((Publisher)sender).Name == "John Smith")
{
// ...
}
}
}
つまり、イベントハンドラが異なる (あるいは未知の) オブジェクトタイプからのイベントを購読する必要がある場合、ハンドラは 'sender' パラメータを 'object' とタイプして、潜在的な送信者オブジェクトの全範囲を処理することができます。
慣習を破ること (これは、私を信じて軽く考えないことです) 以外には、これに対するマイナス面を思いつきません。
ここでは、いくつかの CLS 準拠の問題があるかもしれません。これは Visual Basic .NET 2008 では 100% 問題なく実行できますが (テスト済み)、2005 までの古いバージョンの Visual Basic .NET では、代表団の共変動と共変動がないものと思われます。 [編集: その後テストしたところ、確認できました。VB.NET 2005以下はこれを処理できませんが、VB.NET 2008は100%問題ありません。以下の "Edit #2" を参照してください]。 他の.NET言語でも問題があるかもしれませんが、確証はありません。
しかし、私は C# または Visual Basic .NET 以外の言語で開発することは考えていませんし、.NET Framework 3.0 以上の C# と VB.NET に制限することに抵抗はありません(.NET Framework 3.0 以降に戻ることは考えられません)。(正直なところ、この時点で 2.0 に戻ることは考えられません)。
誰かこれの問題点を思いつく人はいますか?それとも、これは単に、人々の胃を回転させるほど慣習を破っているのでしょうか?
以下は、私が見つけた関連リンクです。
(2) C# simple Event Raising - using "sender" vs. custom EventArgs [StackOverflow 2009].
(3) .netのイベントシグネチャパターン [StackOverflow 2008].
私はこれについての誰か、そして皆の意見に興味があります...。
よろしくお願いします。
マイク
1を編集します。 これは トミー・カルリエの投稿 :
ここに、強い型のイベントハンドラと「オブジェクト送信者」パラメータを使用する現在の標準的なイベントハンドラの両方が、このアプローチと共存できることを示す完全な動作例を示します。コードをコピーペーストして、実行することができます。
namespace csScrap.GenericEventHandling
{
class PublisherEventArgs : EventArgs
{
// ...
}
[SerializableAttribute]
public delegate void StrongTypedEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
class Publisher
{
public event StrongTypedEventHandler<Publisher, PublisherEventArgs> SomeEvent;
public void OnSomeEvent()
{
if (SomeEvent != null)
{
SomeEvent(this, new PublisherEventArgs());
}
}
}
class StrongTypedSubscriber
{
public void SomeEventHandler(Publisher sender, PublisherEventArgs e)
{
MessageBox.Show("StrongTypedSubscriber.SomeEventHandler called.");
}
}
class TraditionalSubscriber
{
public void SomeEventHandler(object sender, PublisherEventArgs e)
{
MessageBox.Show("TraditionalSubscriber.SomeEventHandler called.");
}
}
class Tester
{
public static void Main()
{
Publisher publisher = new Publisher();
StrongTypedSubscriber strongTypedSubscriber = new StrongTypedSubscriber();
TraditionalSubscriber traditionalSubscriber = new TraditionalSubscriber();
publisher.SomeEvent += strongTypedSubscriber.SomeEventHandler;
publisher.SomeEvent += traditionalSubscriber.SomeEventHandler;
publisher.OnSomeEvent();
}
}
}
2番を編集します。 これは Andrew Hareの発言 に対するもので、それがどのようにここで適用されるかについてです。C# 言語のデリゲートには長い間、共変と反変があり、それは本質的なものだと感じていますが、そうではありません。しかし、Visual Basic .NETでは、.NET Framework 3.0 (VB.NET 2008) になるまで、デリゲートに共変および共変量機能が搭載されませんでした。そしてその結果、.NET 2.0 以下の Visual Basic.NET では、このアプローチを利用することができないでしょう。
例えば、上記の例をVB.NETに置き換えると、以下のようになります。
Namespace GenericEventHandling
Class PublisherEventArgs
Inherits EventArgs
' ...
' ...
End Class
<SerializableAttribute()> _
Public Delegate Sub StrongTypedEventHandler(Of TSender, TEventArgs As EventArgs) _
(ByVal sender As TSender, ByVal e As TEventArgs)
Class Publisher
Public Event SomeEvent As StrongTypedEventHandler(Of Publisher, PublisherEventArgs)
Public Sub OnSomeEvent()
RaiseEvent SomeEvent(Me, New PublisherEventArgs)
End Sub
End Class
Class StrongTypedSubscriber
Public Sub SomeEventHandler(ByVal sender As Publisher, ByVal e As PublisherEventArgs)
MessageBox.Show("StrongTypedSubscriber.SomeEventHandler called.")
End Sub
End Class
Class TraditionalSubscriber
Public Sub SomeEventHandler(ByVal sender As Object, ByVal e As PublisherEventArgs)
MessageBox.Show("TraditionalSubscriber.SomeEventHandler called.")
End Sub
End Class
Class Tester
Public Shared Sub Main()
Dim publisher As Publisher = New Publisher
Dim strongTypedSubscriber As StrongTypedSubscriber = New StrongTypedSubscriber
Dim traditionalSubscriber As TraditionalSubscriber = New TraditionalSubscriber
AddHandler publisher.SomeEvent, AddressOf strongTypedSubscriber.SomeEventHandler
AddHandler publisher.SomeEvent, AddressOf traditionalSubscriber.SomeEventHandler
publisher.OnSomeEvent()
End Sub
End Class
End Namespace
VB.NET 2008では100%問題なく実行できます。しかし、今、念のためVB.NET 2005でテストしてみましたが、コンパイルできません、状態です。
メソッド 'Public Sub SomeEventHandler(sender As Object, e As vbGenericEventHandling.GenericEventHandling.PublisherEventArgs)' です。 と同じシグネチャを持ちません。 delegate 'デリゲートサブ StrongTypedEventHandler(Of TSender, TEventArgs As System.EventArgs)(sender As Publisher, e As PublisherEventArgs)'
基本的に、デリゲートはVB.NETのバージョン2005以下では不変のものです。実はこのアイデアは数年前に考えたのですが、VB.NETがこれに対応できないことが気になっていました...。でも、今はしっかりC#に移行して、VB.NETでも扱えるようになったので、まあ、この投稿になったわけです。
編集:アップデートその3
OK、私はしばらくの間、これをかなりうまく使っています。本当にいいシステムだと思います。StrongTypedEventHandler"をGenericEventHandler"と名付け、以下のように定義することにしました。
[SerializableAttribute]
public delegate void GenericEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
この名前の変更以外は、上述と全く同じように実装しました。
これは、FxCop 規則 CA1009 に抵触します。
慣例により、.NET イベントには、イベントを指定する 2 つのパラメータがあります。 イベントの送信者とイベント データを指定する 2 つのパラメータがあります。 送信者とイベント データを指定します。イベントハンドラ のシグネチャは、この形式に従わなければなりません。 void MyEventHandler( object sender, EventArgs e)。sender' パラメータ は常に System.Object 型です。 より具体的な型を採用することが可能であったとしても 型です。e' パラメータは は常に System.EventArgs 型です。 イベントデータを提供しないイベント は System.EventHandler デリゲートタイプを使用します。イベントハンドラは を返すので、各イベントを複数のターゲットメソッドに送ることができます。 を複数のターゲットメソッドに送ることができます。ターゲットが返すいかなる値 ターゲットから返された値は、最初の呼び出しの後、失われます。 によって返される値は、最初の呼び出しの後に失われます。
もちろん、私たちはこのことをすべて知っていて、とにかく規則を破っています。(すべてのイベント ハンドラは、どのような場合でも優先されるなら、標準の 'object Sender' をその署名で使用できます。) これは違反ではない変更です。
ですから
SuppressMessageAttribute
を使うとうまくいきます。
[SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly",
Justification = "Using strong-typed GenericEventHandler<TSender, TEventArgs> event handler pattern.")]
将来的には、この方法が標準になることを願っています。本当にうまく機能するのです。
皆さん、たくさんのご意見ありがとうございました。
マイク
どのように解決するのですか?
Microsoft がこれに気づいたようで、同様の例が MSDN に掲載されています。
関連
-
[解決済み] 'SubSonic.Schema .DatabaseColumn' 型のオブジェクトをシリアライズする際に、循環参照が検出されました。
-
[解決済み】プロジェクトビルド時のエラー。エディタでスクリプトにコンパイルエラーがあるため、Playerのビルドにエラーが発生する
-
[解決済み】ORA-01008: すべての変数がバインドされていません。これらはバインドされています。
-
[解決済み】取り消せないメンバはメソッドのように使えない?
-
[解決済み】EF 5 Enable-Migrations : アセンブリにコンテキストタイプが見つかりませんでした
-
[解決済み】aspNetCore 2.2.0 - AspNetCoreModuleV2 エラー
-
[解決済み] [Solved] .NETでスレッドの終了を待つには?
-
[解決済み】「namespace」なのに「type」のように使われる。
-
[解決済み】Microsoft.Extensions.LoggingからILoggerを解決することができない
-
[解決済み] C#を使用して.NETで現在のユーザー名を取得する方法は?
最新
-
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#
-
[解決済み】コンパイルエラー「未割り当てのローカル変数を使用しています」が発生したのはなぜですか?
-
[解決済み】文字列が有効な DateTime " format dd/MM/yyyy " として認識されなかった。
-
[解決済み】バックスラッシュを含むパス文字列のエスケープシーケンスが認識されない件
-
[解決済み】C# - パスに不正な文字がある場合
-
[解決済み] 'IEnumerable<SelectListItem>' 型の ViewData アイテムで、キーが国であるものは存在しない。
-
[解決済み】なぜこのコードはInvalidOperationExceptionを投げるのですか?
-
[解決済み】Visual studio 2019がデバッグ時にフリーズする件
-
[解決済み】「...は'型'であり、与えられたコンテキストでは有効ではありません」を解決するにはどうすればよいですか?(C#)
-
[解決済み】URLから画像をダウンロードする方法