I am programming a software in C# at work that contains 2 Threads
I need the second thread to print a massage on the form when the online data is irregular.
because only the thread that created the control can change it, I am using delegates. the second thread calls the first thread to execute a delegate by the Control.Invoke method.
Example:
public partial class Form1 : Form
{
public delegate void SafeCallDelegate(string text);
public static SafeCallDelegate d;
public Form1()
{
InitializeComponent();
d = new SafeCallDelegate(addText);
}
private static void addText(string text)
{
richTextBox1.Text += text + "\n";
}
}
static class Program
{
static Thread secondThread;
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
secondThread = new Thread(SecondThreadFunc);
secondThread.Start();
Application.Run(new Form1());
}
static void SecondThreadFunc()
{
int counter = 0;
do
{
if (Form.ActiveForm == null) continue;
Form.ActiveForm.Invoke(Form1.d, new object[] { "SecondThreadFunc counter: " + counter.ToString() });
counter++;
Thread.Sleep(1000);
} while (true);
}
}
I can live with this not very clean solution, but my problem is that this is not type-safe at all.
the Control.Invoke function takes an array of objects, regardless of what the delegate requires and this can result in a run-time exception.
Is there a method to use that is more type-safe and can solve my problem?
Thank you.
Instead of passing the arguments to Invoke
, pass them as a closured variable within the delegate.
So, instead of this:
Form.ActiveForm.Invoke(Form1.d, new object[] { "SecondThreadFunc counter: " + counter.ToString() });
Write this:
Form.ActiveForm.Invoke
(
new Action
(
() => Form1.d("SecondThreadFunc counter: " + counter.ToString())
)
);
This avoids the problem of passing arguments to Invoke
and eliminates the type-safety issue.
If that seems a little wordy to you, you can also define a simple helper extension method:
static class Helpers
{
static public void MyInvoke(this Control control, Action action)
{
if (control.InvokeRequired)
{
control.Invoke(action);
}
else
{
action();
}
}
}
Now all you have to write in your main form is this:
this.MyInvoke( () => addText("SecondThreadFunc counter: " + counter.ToString()));
You can now get rid of all that SafeCallDelegate stuff and just pass whatever lambda you want when you need it.