タイマーを使用してメソッドのイベントを遅延させようとしていますが、タイマーを使用して待機する方法が必ずしもわかりません。
タイマーを2秒に設定しましたが、このコードを実行すると、最後の呼び出しは2秒の遅延なしで実行されます。
Timer timer = new Timer();
timer.Tick += new EventHandler(timer_Tick); // Everytime timer ticks, timer_Tick will be called
timer.Interval = (1000) * (2); // Timer will tick evert second
timer.Enabled = true; // Enable the timer
void timer_Tick(object sender, EventArgs e)
{
timer.Stop();
}
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "first";
timer.Start();
label1.Text = "second";
}
したがって、ボタンをクリックすると、label1がすぐに「秒」として表示されます。「最初」に変更して2秒待ってから「秒」に変更するのではありません。 thread.sleepの代わりにタイマーを使用することについて、ここで多くのスレッドを読みましたが、実際にそれを実装する方法を見つけたり、図にしたりすることができません。
timer.Start()
はタイマーを開始するだけですが、タイマーがバックグラウンドで実行されている間はすぐに戻ります。したがって、ラベルテキストをfirst
とsecond
に設定する間に一時停止はほとんどありません。あなたがしたいことは、タイマーが作動するのを待ってから、もう一度ラベルを更新することです:
_void timer_Tick(object sender, EventArgs e)
{
timer.Stop();
label1.Text = "second";
}
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "first";
timer.Start();
}
_
ところで_timer.Enabled
_をtrueに設定しないでください。すでにtimer.Start()
を使用してタイマーを開始しています。
コメントで述べたように、タイマーの作成を次のようにメソッドに入れることができます(注:これはテストされていません)。
_public void Delayed(int delay, Action action)
{
Timer timer = new Timer();
timer.Interval = delay;
timer.Tick += (s, e) => {
action();
timer.Stop();
};
timer.Start();
}
_
そして、あなたはこのようにそれを使うことができます:
_private void button1_Click(object sender, EventArgs e)
{
label1.Text = "first";
Delayed(2000, () => label1.Text = "second");
}
_
Delayedを使用すると、メモリリーク(参照リーク)が発生しますか?
イベントをサブスクライブすると、常に双方向の参照が作成されます。
この場合、_
timer.Tick
_は無名関数(lambda)への参照を取得します。この関数はローカル変数timer
を持ち上げますが、これは値ではなく参照であり、渡されたアクションデリゲートへの参照を含みます。そのデリゲートには、Form
のインスタンスメンバーである_label1
_への参照が含まれます。では、Timer
からForm
への循環参照はありますか?答えはわかりませんが、理由を説明するのは少し難しいと思います。わからないので、
Delayed
でのラムダの使用を削除し、適切なメソッドにして、タイマー(メソッドのsender
パラメーター)を停止することに加えて、イベントも削除します。
通常、ラムダはガベージコレクションに問題を引き起こしません。この場合、タイマーインスタンスはローカルにのみ存在し、ラムダ内の参照は、ガベージコレクションによるインスタンスの収集を妨げません( この質問 も参照)。
.NET Memory Profilerを使用して、これを実際にテストしました。タイマーオブジェクトは問題なく収集され、リークは発生しませんでした。プロファイラーから、「[...]が適切に廃棄されずにガベージコレクションされている」場合があるという警告が表示されました。 (それへの参照を維持することによって)イベントハンドラ自体を削除しても、それは修正されませんでした。キャプチャしたタイマー参照を_(Timer)s
_に変更しても、変更されませんでした。
明らかに何が役に立ったかは、タイマーを停止した後、イベントハンドラーでtimer.Dispose()
を呼び出すことでしたが、それが実際に必要かどうかを私は主張します。プロファイラーの警告/メモはそれほど重要ではないと思います。
C#5.0を使用している場合、await
を使用するとmuchが簡単になります。
private async void button1_Click(object sender, EventArgs e)
{
label1.Text = "first";
await Task.Delay(2000);
label1.Text = "second";
}
private bool Delay(int millisecond)
{
Stopwatch sw = new Stopwatch();
sw.Start();
bool flag = false;
while (!flag)
{
if (sw.ElapsedMilliseconds > millisecond)
{
flag = true;
}
}
sw.Stop();
return true;
}
bool del = Delay(1000);
タイマーがカチカチ音を立てたときにテキストを変更するだけの場合は、プットオフするほうがいいでしょう...
label1.Text = "second";
...タイマーティックで、タイマーをenabled = falseに変更する前または後。
そのようです;
void timer_Tick(object sender, EventArgs e)
{
timer.Stop();
label1.Text = "second";
}
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "first";
timer.Start();
}