null reference problems with c#
In one of my window form, I created an instance of a class to do some works in the background. I wanted to capture the debug messages in that class and displayed in the textbox in the window form. Here is what I did:
class A //window f开发者_StackOverfloworm class
{
public void startBackGroundTask()
{
B backGroundTask = new B(this);
}
public void updateTextBox(string data)
{
if (data != null)
{
if (this.Textbox.InvokeRequired)
{
appendUIDelegate updateDelegate = new appendUIDelegate(updateUI);
try
{
this.Invoke(updateDelegate, data);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
else
{
updateUI(data);
}
}
}
private void updateUI(string data)
{
if (this.Textbox.InvokeRequired)
{
this.Textbox.Invoke(new appendUIDelegate(this.updateUI), data);
}
else
{
//update the text box
this.Textbox.AppendText(data);
this.Textbox.AppendText(Environment.NewLine);
}
}
private delegate void appendUIDelegate(string data);
}
class B // background task
{
A curUI;
public b( A UI)
{
curUI = UI;
}
private void test()
{
//do some works here then log the debug message to UI.
curUI.updateTextBox("message);
}
}
I keep getting a null reference exception after
this.Invoke(updateDelegate, data);
is called.
I know passing "this" as a parameter is strange, but I want to send the debug message to my window form.
Two things:
1) Consider using an extension method so you don't repeat yourself on all those invokes:
public static class ControlExtentions
{
public delegate void InvokeHandler();
public static void SafeInvoke(this Control control, InvokeHandler handler)
{
if (control.InvokeRequired)
control.Invoke(handler);
else
handler();
}
}
2) Consider using events to make this more readable. Use something like this design:
class B
{
public event EventHandler LogGenerated = delegate { };
void test()
{
LogGenerated("Some log text", EventArgs.Empty);
}
}
class A : Control
{
public A()
{
B b = new B();
b.LogGenerated += new EventHandler(b_LogGenerated);
}
void b_LogGenerated(object sender, EventArgs e)
{
this.SafeInvoke(() => { textBox1.Text += (String)sender; });
}
}
Don't let that syntax of calling SafeInvoke throw you too much; it's simply a lambda that represents an InvokeHandler delegate.
The logic you've described in the question does not produce a null reference exception, which suggests that the exception is coming from the background task you're executing rather than from the UI update logic.
As evidence, here's a full sample that works which is simplified, but based on the pattern you described:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void updateTextBox(string data)
{
if (this.textBox1.InvokeRequired)
{
this.Invoke(new MethodInvoker(() => updateTextBox(data)));
return;
}
if (data == null)
{
return;
}
//update the text box
this.textBox1.AppendText(data);
this.textBox1.AppendText(Environment.NewLine);
}
private void _uxStartBgTask_Click(object sender, EventArgs e)
{
new Thread(() => updateTextBox("message")).Start();
}
}
Note that the use of MethodInvoker eliminates the need to declare a delegate member and that immediate recursion is a convenient way of addressing the InvokeRequired pattern.
It looks like you have an infinite loop. updateTextBox constructs a delegate to call updateUI, and updateUI constructs a delegate to updateUI. What prevents infinite recursion here?
精彩评论