Robert said:
As you might guess from the title, I'm a C# newbie, though certainly not
a programming newbie. What I'm trying to set up is basically an
asynchronous class, but I'm a little lost as to which method is
appropriate and how to set it up.
From what I've seen, using the BeginInvoke style of converting a
synchronous call to asynchronous isn't going to work here, but maybe I'm
just doing it wrong.
For a true "asynchronous operation" scenario, using the
Delegate.BeginInvoke() method is in fact one of the simplest ways to get
things working. But a) from your description you don't really seem to
have exactly that scenario, and b) it's possible you're seeing the
Control.BeginInvoke() method instance and that's confusing things.
So for simplicity's sake, let's say I've got a class that has the
following:
* a StatusUpdate event that returns a status string to listeners
Easy, but has very little to do with the asynchronous aspect, other than
the event handler may need special logic to deal with
cross-thread/synchronization issues.
* a Stop method
Okay.
* an asynchronous DoSomething method that loops infinitely, updating the
status with each iteration until the Stop method is called.
How often is the status updated? Is the method something that would
loop infinitely normally? Or is the work represented in some other
class, as a single one-shot (non-looping) operation?
Barring any other information, your scenario seems to call for the use
of some kind of Timer class. If you're writing a Forms application,
then the System.Windows.Forms.Timer class is probably most appropriate.
Otherwise, you may want to use System.Threading.Timer instead.
One significant advantage to using the System.Windows.Forms.Timer class,
if you can, is that it raises the Tick event on the main GUI thread,
which not only avoids the need to deal with the Control.Invoke() method,
also essentially keeps all the related code single-threaded, so you also
don't wind up needing to deal with thread synchronization issues.
That would be used by a form that basically calls DoSomething, updates a
text box with the string returned from SatusUpdate whenever the event is
raised, and a Stop button to stop whenever the user decides.
If you use a Timer class, then the above amounts to doing this:
– Create the Timer instance
– Set the Timer up to execute the code that would be in the
DoSomething() method's loop each time the timer fires
– Raise the event as desired in that code
– Stop the Timer when the Stop button is clicked
Actual code for the above would be REALLY nice, of course, but even if
someone could just point me in the right general direction of the
concepts I need to look at, it'd be appreciated.
If you are more specific about your question, you can get more specific
information in the answer to your question.
In the meantime, here are a few possible implementation examples that
may or may not be applicable to your specific scenario (caveat:
incomplete, uncompiled, untested code)...
Plain, threading example:
class Form1 : Form
{
Class1 _class1;
void buttonStart_Click(object sender, EventArgs e)
{
_class1 = new Class1();
_class1.StatusUpdate += class1_StatusUpdate;
new Thread(_class1.DoSomething).Start();
buttonStart.Enabled = false;
buttonStop.Enabled = true;
}
void buttonStop_Click(object sender, EventArgs e)
{
_class1.Stop();
buttonStart.Enabled = true;
buttonStop.Enabled = false;
}
void class1_StatusUpdate(string str)
{
label1.Invoke(_UpdateLabel, new object[] { str });
}
void _UpdateLabel(string str)
{
label1.Text = str;
}
}
class Class1
{
public void DoSomething()
{
int i = 0;
while (!Done)
{
Thread.Sleep(250);
Action(i++.ToString());
}
}
public void Stop()
{
Done = true;
}
public bool Done
{
get { return _fDone; }
private set { _fDone = value; }
}
volatile bool _fDone;
public event Action<string> StatusUpdate = delegate { };
}
Plain, threading example, where the actual work involves a non-looping
class:
class Form1 : Form
{
void buttonStart_Click(object sender, EventArgs e)
{
_fDone = false;
new Thread(DoSomethingLoop).Start();
buttonStart.Enabled = false;
buttonStop.Enabled = true;
}
void DoSomethingLoop()
{
Class1 class1 = new Class1();
int i = 0;
while (!_fDone)
{
Thread.Sleep(250);
class1_StatusUpdate(class1.DoSomething(i++));
}
}
volatile bool _fDone;
void buttonStop_Click(object sender, EventArgs e)
{
_fDone = true;
buttonStart.Enabled = true;
buttonStop.Enabled = false;
}
void class1_StatusUpdate(string str)
{
label1.Invoke(_UpdateLabel, str);
}
void _UpdateLabel(string str)
{
label1.Text = str;
}
}
class Class1
{
// Just a simple operation returning a string
public string DoSomething(int i)
{
return i.ToString();
}
}
Timer-based example:
class Form1 : Form
{
System.Windows.Forms.Timer _timer = new System.Windows.Forms.Timer();
void buttonStart_Click(object sender, EventArgs e)
{
Class1 class1 = new Class1();
int i = 0;
// Tick event handler is executed on main GUI thread,
// so no call to Control.Invoke() is needed in order
// to update the label1.Text property:
_timer.Tick += (sender, e) =>
{
label1.Text = class1.DoSomething(i++);
};
_timer.Interval = 250;
_timer.Start();
buttonStart.Enabled = false;
buttonStop.Enabled = true;
}
void buttonStop_Click(object sender, EventArgs e)
{
_timer.Stop();
buttonStart.Enabled = true;
buttonStop.Enabled = false;
}
}
class Class1
{
// Just a simple operation returning a string
public string DoSomething(int i)
{
return i.ToString();
}
}
That last example has a little bit of sneakiness in it. In particular,
this section of code:
Class1 class1 = new Class1();
int i = 0;
_timer.Tick += (sender, e) =>
{
label1.Text = class1.DoSomething(i++);
};
…hides some fairly elaborate compiler-generated code. The first sneaky
bit is the "(sender, e) => { ... }" part. This is a lambda expression
with a statement body, used to declare an anonymous method. Rather than
having a named method to subscribe to the Tick event as the handler,
this unnamed method is used instead.
The next bit is that the variables "class1" and "i" act syntactically as
local variables but in reality, because they are used within the
anonymous method, become "captured" along with the anonymous method.
This means that the lifetime of those variables is extended to that of
the anonymous method instance, so the variables continue to exist even
after the method where they are declared returns.
This allows them to maintain state necessary for the Tick event handler.
Specifically, the Class1 instance reference, along with the counter.
In the first two examples, I avoided using any anonymous methods (*)
because they can sometimes confuse people new to C#. But IMHO they are
a really useful and important thing to learn, because they allow all
related code and variables to remain together in the same declared
method. This helps the code to be more readable, and more concise at
the same time.
Both of the first two examples could also very easily take advantage of
anonymous methods, by using them for the StatusUpdate event handler (in
the first example), and for the thread entry point method and delegate
used for the Control.Invoke() method (in both examples).
If none of this seems to be addressing what you are trying to do, try
posting a less vague question, preferably one that includes code itself.
Pete
(*) (Actually, I lied. There is in fact an anonymous method in the
first code example. See if you can find it!
![Smile :) :)](/styles/default/custom/smilies/smile.gif)
)