C#のイベント/デリゲートについて学んでいます。私が選択した命名/コーディングスタイル(Head First C#の本から引用)について意見を伺えますか?
明日、これについて友人に教えており、コンセプトを説明する最もエレガントな方法を考えています。 (主題を理解する最良の方法は、それを試して教えることだと考えました!)
class Program
{
static void Main()
{
// setup the metronome and make sure the EventHandler delegate is ready
Metronome metronome = new Metronome();
// wires up the metronome_Tick method to the EventHandler delegate
Listener listener = new Listener(metronome);
metronome.OnTick();
}
}
public class Metronome
{
// a delegate
// so every time Tick is called, the runtime calls another method
// in this case Listener.metronome_Tick
public event EventHandler Tick;
public void OnTick()
{
while (true)
{
Thread.Sleep(2000);
// because using EventHandler delegate, need to include the sending object and eventargs
// although we are not using them
Tick(this, EventArgs.Empty);
}
}
}
public class Listener
{
public Listener(Metronome metronome)
{
metronome.Tick += new EventHandler(metronome_Tick);
}
private void metronome_Tick(object sender, EventArgs e)
{
Console.WriteLine("Heard it");
}
}
n.b.コードは http://www.codeproject.com/KB/cs/simplesteventexample.aspx からリファクタリングされます
私が言及するいくつかのポイントがあります:
Metronome.OnTickの名前が正しくないようです。意味的には、「OnTick」は、「Tick」が発生したときに呼び出されることを示していますが、実際にはそうではありません。代わりに「Go」と呼びます。
ただし、一般的に受け入れられているモデルは、次のことです。 OnTick
は、イベントを発生させる仮想メソッドです。このようにして、継承されたクラスのデフォルトの動作を簡単にオーバーライドし、ベースを呼び出してイベントを発生させることができます。
_class Metronome
{
public event EventHandler Tick;
protected virtual void OnTick(EventArgs e)
{
//Raise the Tick event (see below for an explanation of this)
var tickEvent = Tick;
if(tickEvent != null)
tickEvent(this, e);
}
public void Go()
{
while(true)
{
Thread.Sleep(2000);
OnTick(EventArgs.Empty); //Raises the Tick event
}
}
}
_
また、これは単純な例ですが、リスナーが接続されていない場合、コードはTick(this, EventArgs.Empty)
でスローします。リスナーを確認するには、少なくともヌルガードを含める必要があります。
_if(Tick != null)
Tick(this, EventArgs.Empty);
_
ただし、これは、ガードと呼び出しの間でリスナーが登録解除されている場合、マルチスレッド環境では依然として脆弱です。最善の方法は、最初に現在のリスナーをキャプチャして呼び出します。
_var tickEvent = Tick;
if(tickEvent != null)
tickEvent(this, EventArgs.Empty);
_
私はこれが古い答えであることを知っていますが、まだ賛成票を集めているので、ここでC#6のやり方を紹介します。 「ガード」概念全体を条件付きメソッド呼び出しに置き換えることができ、コンパイラーは実際にリスナーのキャプチャに関してRight Thing(TM)を実行します。
_Tick?.Invoke(this, EventArgs.Empty);
_
マイクロソフトは実際に広範な命名ガイドラインを作成し、MSDNライブラリに追加しました。ここで記事を見つけることができます: 名前のガイドライン
一般的な大文字化のガイドラインとは別に、ページの「イベント」で使用できるものを次に示します Names of Type Members :
イベントに動詞または動詞句を付けてください。
現在および過去の時制を使用して、イベント名に前後の概念を与えてください。たとえば、ウィンドウが閉じる前に発生するcloseイベントはClosingと呼ばれ、ウィンドウが閉じられた後に発生するcloseイベントはClosedと呼ばれます。
イベントの前後にプレフィックスまたはサフィックスを使用しないでください。
EventHandlerサフィックスを使用して、イベントハンドラー(イベントの種類として使用されるデリゲート)に名前を付けます。
イベントハンドラシグネチャでは、senderおよびeという2つのパラメータを使用してください。
SenderパラメーターはObject型である必要があり、eパラメーターはEventArgsのインスタンスであるか、EventArgsから継承する必要があります。
イベント引数クラスにEventArgsサフィックスを付けてください。
命名規則を含む、一般的なイベントのベストガイドは here です。
それは私が採用したコンベンションです、簡単に:
興味深いのは、MicrosoftがVisual Studioが生成したイベントハンドラーの名前に関する独自の命名規則に違反しているように見えることです。
.Netで何年もイベントを使用した後に発見したポイントは、呼び出しごとにnullハンドラーのイベントを繰り返しチェックする必要があることです。イベントがnullの場合、イベントを呼び出さないが、何もしないライブコードをまだ見ていません。
私が始めたのは、作成するすべてのイベントにダミーハンドラーを配置して、nullチェックを行う必要をなくすことです。
public class Metronome
{
public event EventHandler Tick =+ (s,e) => {};
protected virtual void OnTick(EventArgs e)
{
Tick(this, e); // now it's safe to call without the null check.
}
}
OnTick
は典型的なイベント呼び出しモデルに従っていないという事実は別として、見た目は良いです。通常、_On[EventName]
_は次のようにイベントを1回発生させます
_protected virtual void OnTick(EventArgs e)
{
if(Tick != null) Tick(this, e);
}
_
このメソッドを作成し、既存の「OnTick
」メソッドの名前を「StartTick
」に変更し、Tick
から直接StartTick
を呼び出す代わりに、OnTick(EventArgs.Empty)
。
あなたの場合、それは次のようになります:
class Metronome {
event Action Ticked;
internalMethod() {
// bla bla
Ticked();
}
}
上記の例では、以下の規則に従って、自己記述的に使用します;]
イベントソース:
class Door {
// case1: property change, pattern: xxxChanged
public event Action<bool> LockStateChanged;
// case2: pure action, pattern: "past verb"
public event Action<bool> Opened;
internalMethodGeneratingEvents() {
// bla bla ...
Opened(true);
LockStateChanged(false);
}
}
ところで。キーワードevent
はオプションですが、「イベント」と「コールバック」を区別できます
イベントリスナー:
class AlarmManager {
// pattern: NotifyXxx
public NotifyLockStateChanged(bool state) {
// ...
}
// pattern: [as above]
public NotifyOpened(bool opened) {
// OR
public NotifyDoorOpened(bool opened) {
// ...
}
}
そしてバインディング[コードは人間に優しいように見える]
door.LockStateChanged += alarmManager.NotifyLockStateChanged;
door.Moved += alarmManager.NotifyDoorOpened;
手動でイベントを送信することも「人間が読める」ものです。
alarmManager.NotifyDoorOpened(true);
時には、より表現力豊かに「動詞+ ing」することができます
dataGenerator.DataWaiting += dataGenerator.NotifyDataWaiting;
どちらの規則を選択する場合でも、それと一致するようにしてください。