カスタムイベントを学習しようとしています。カスタムイベントを作成しようとしましたが、問題があるようです。
フォーム、静的クラス、カスタムイベントを作成しました。私が達成しようとしているのは、ボタンを押すとFormが静的クラス関数を呼び出し、その後funcがイベントを時々発生させて現在のステータスを報告することです。 Form1は、イベントが発生した場合にリッスンし、発生した場合、label1のテキストを変更します
ここに私がこれまで持っているものがあります
public partial class Form1 : Form
{
public EventHandler<Progress> progress;
public Form1()
{
InitializeComponent();
progress += SetStatus;
}
private void SetStatus(object sender, Progress e)
{
label1.Text = e.Status;
}
private void button1_Click_1(object sender, EventArgs e)
{
TestClass.Func();
}
}
ファイル2
class TestClass
{
public static void Func()
{
//time consuming code
Report status
// time consuming code
report status
}
}
public class Progress : EventArgs
{
public string Status { get; private set; }
private Progress() {}
public Progress(string status)
{
Status = status;
}
}
今私が理解していないことは、Form1がイベントを処理してラベルを変更できるようにTestClassからイベントを発生させる方法です。
これは、カスタムイベントを作成して発生させる簡単な方法です。スローするクラスにデリゲートとイベントを作成します。次に、コードの別の部分からイベントをサブスクライブします。既にカスタムイベント引数クラスを持っているので、それを基に他のイベント引数クラスを作成できます。 N.B:このコードはコンパイルしていません。
public partial class Form1 : Form
{
private TestClass _testClass;
public Form1()
{
InitializeComponent();
_testClass = new TestClass();
_testClass.OnUpdateStatus += new TestClass.StatusUpdateHandler(UpdateStatus);
}
private void UpdateStatus(object sender, ProgressEventArgs e)
{
SetStatus(e.Status);
}
private void SetStatus(string status)
{
label1.Text = status;
}
private void button1_Click_1(object sender, EventArgs e)
{
TestClass.Func();
}
}
public class TestClass
{
public delegate void StatusUpdateHandler(object sender, ProgressEventArgs e);
public event StatusUpdateHandler OnUpdateStatus;
public static void Func()
{
//time consuming code
UpdateStatus(status);
// time consuming code
UpdateStatus(status);
}
private void UpdateStatus(string status)
{
// Make sure someone is listening to event
if (OnUpdateStatus == null) return;
ProgressEventArgs args = new ProgressEventArgs(status);
OnUpdateStatus(this, args);
}
}
public class ProgressEventArgs : EventArgs
{
public string Status { get; private set; }
public ProgressEventArgs(string status)
{
Status = status;
}
}
イベントを作成していません。それを行うには:
public event EventHandler<Progress> Progress;
次に、通常の関数またはデリゲートのように宣言されたクラス内からProgress
を呼び出すことができます。
Progress(this, new Progress("some status"));
したがって、TestClass
の進行状況を報告する場合は、イベントもそこにあり、静的である必要があります。次のようにフォームからサブスクライブできます。
TestClass.Progress += SetStatus;
また、Progress
の名前をProgressEventArgs
に変更して、内容を明確にする必要があります。
C#でのイベントは非常に簡単ですが、私の意見では、MSDNのドキュメントを見るとイベントがかなり混乱しています。通常、表示されるほとんどのドキュメントでは、クラスをEventArgs
基本クラスから継承し、a reasonそれ。ただし、これはイベントを作成する最も簡単な方法ではなく、何かをすばやく簡単にしたい人にとっては、時間のクランチでは、Action
タイプを使用することがチケットです。
1. class
宣言の直後にクラスでイベントを作成します。
public event Action<string,string,string,string>MyEvent;
2.クラスにイベントハンドラクラスメソッドを作成します。
private void MyEventHandler(string s1,string s2,string s3,string s4)
{
Console.WriteLine("{0} {1} {2} {3}",s1,s2,s3,s4);
}
3.ここで、クラスが呼び出されたら、新しいイベントハンドラーにイベントを接続するように指示します。 +=
演算子が使用される理由は、特定のイベントハンドラーをイベントに追加しているためです。実際には、複数の個別のイベントハンドラーでこれを行うことができ、イベントが発生すると、各イベントハンドラーは追加した順序で動作します。
class Example
{
public Example() // I'm a C# style class constructor
{
MyEvent += new Action<string,string,string,string>(MyEventHandler);
}
}
4.準備ができたら、クラスコードのどこかでイベントをトリガー(別名)します:
MyEvent("wow","this","is","cool");
これを実行すると、コンソールから「すごいすごい」と出力されます。日付またはシーケンスで「クール」を変更し、このイベントトリガーを複数回実行すると、イベントが通常動作するようにFIFOシーケンスで結果が表示されます。
この例では、4つの文字列を渡しました。ただし、これらを任意の種類の許容可能なタイプに変更したり、使用するタイプを増減したり、<...>
outを削除してイベントハンドラーに何も渡すことはできません。
また、複数のカスタムイベントハンドラーがあり、+=
演算子を使用してそれらをすべてイベントにサブスクライブした場合、イベントトリガーはそれらをすべて順番に呼び出します。
しかし、イベントハンドラーでこのイベントの呼び出し元を特定する場合はどうでしょうか。これは、誰がイベントを発生/トリガーしたかに基づいて条件に反応するイベントハンドラーが必要な場合に便利です。これを行うにはいくつかの方法があります。以下は、動作速度の順に示されている例です。
オプション1(最速)既にわかっている場合は、トリガー時にイベントハンドラーに名前をリテラル文字列として渡します。
オプション2(やや高速)これをクラスに追加し、呼び出し元のメソッドから呼び出して、トリガーしたときにその文字列をイベントハンドラーに渡します。
private static string GetCaller([System.Runtime.CompilerServices.CallerMemberName] string s = null) => s;
オプション3(最速ですが、まだ高速)イベントハンドラーでトリガーするとき、これで呼び出しメソッド名の文字列を取得します。
string callingMethod = new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().ReflectedType.Name.Split('<', '>')[1];
カスタムイベントに複数のイベントハンドラーがあるシナリオがありますが、イベントハンドラーのリストから特別なイベントハンドラーを1つ削除したい場合があります。そのためには、次のように-=
演算子を使用します。
MyEvent -= MyEventHandler;
ただし、これには若干の注意が必要です。これを行うと、そのイベントにイベントハンドラーがなくなって、そのイベントを再度トリガーすると、例外がスローされます。 (もちろん、例外はtry/catchブロックでトラップできます。)
さて、あなたはイベントを終了し、これ以上処理したくないとしましょう。次のようにnullに設定するだけです。
MyEvent = null;
サブスクリプション解除イベントについても同じ注意がここにあります。カスタムイベントハンドラーにイベントがなくなったときに再度トリガーすると、プログラムは例外をスローします。
すでに述べたように、進捗フィールドにはキーワードイベントが必要です
public event EventHandler<Progress> progress;
しかし、私はあなたが実際にあなたのイベントが欲しい場所だとは思わない。あなたは実際にTestClass
のイベントを望んでいると思います。次はどのように見えますか? (私は実際に静的イベントを設定しようとしたことがないので、以下がコンパイルされるかどうかはわかりませんが、これはあなたが目指すべきパターンのアイデアを与えると思います。)
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
TestClass.progress += SetStatus;
}
private void SetStatus(object sender, Progress e)
{
label1.Text = e.Status;
}
private void button1_Click_1(object sender, EventArgs e)
{
TestClass.Func();
}
}
public class TestClass
{
public static event EventHandler<Progress> progress;
public static void Func()
{
//time consuming code
OnProgress(new Progress("current status"));
// time consuming code
OnProgress(new Progress("some new status"));
}
private static void OnProgress(EventArgs e)
{
if (progress != null)
progress(this, e);
}
}
public class Progress : EventArgs
{
public string Status { get; private set; }
private Progress() {}
public Progress(string status)
{
Status = status;
}
}