Как будто вы пытаетесь получить доступ к объекту, который является null
. Рассмотрим ниже пример:
TypeA objA;
. В это время вы только что объявили этот объект, но не инициализировали или не инициализировали. И всякий раз, когда вы пытаетесь получить доступ к каким-либо свойствам или методам в нем, он будет генерировать NullPointerException
, что имеет смысл.
См. Также этот пример:
String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
Для упрощения кода Simon немного Вы могли использовать созданный в универсальном делегате Действия. Это сохраняет усыпание Вашего кода с набором типов делегата, в которых Вы действительно не нуждаетесь. Кроме того, в.NET 3.5 они добавили параметр параметрических усилителей к Вызвать методу, таким образом, Вы не должны определять временный массив.
void SomethingHappened(object sender, EventArgs ea)
{
if (InvokeRequired)
{
Invoke(new Action<object, EventArgs>(SomethingHappened), sender, ea);
return;
}
textBox1.Text = "Something happened";
}
Вот угловые точки:
Так, если Вы имеете отдельный поток "механизма", делающий некоторую работу, и имеете некоторый UI, наблюдающий за изменениями состояния, которые могут быть отражены в UI (таком как индикатор выполнения или безотносительно), у Вас есть проблема. Огонь механизма объект изменил событие, которое было сцеплено Формой. Но делегат обратного вызова, что к Форме, зарегистрированной в механизме, обращаются thread†механизма ¦ не на потоке Формы. И таким образом, Вы не можете обновить средства управления от того обратного вызова. Doh!
BeginInvoke приходит на помощь. Просто используйте эту простую модель кодирования во всех своих методах обратного вызова, и можно быть уверены, что вещи будут хорошо:
private delegate void EventArgsDelegate(object sender, EventArgs ea);
void SomethingHappened(object sender, EventArgs ea)
{
//
// Make sure this callback is on the correct thread
//
if (this.InvokeRequired)
{
this.Invoke(new EventArgsDelegate(SomethingHappened), new object[] { sender, ea });
return;
}
//
// Do something with the event such as update a control
//
textBox1.Text = "Something happened";
}
Это довольно просто действительно.
Это - очень компактный способ решить эту проблему и сделать Ваши Формы безопасными от многопоточных обратных вызовов события.
Я использую анонимные методы много в этом сценарии:
void SomethingHappened(object sender, EventArgs ea)
{
MethodInvoker del = delegate{ textBox1.Text = "Something happened"; };
InvokeRequired ? Invoke( del ) : del();
}
Я немного опаздываю к этой теме, но Вы могли бы хотеть смотреть на Основанный на событии Асинхронный Шаблон . Когда реализовано правильно, это гарантирует, что события всегда генерируются от потока UI.
Вот краткий пример, который только позволяет один параллельный вызов; поддержка нескольких вызовов/событий требует немного большего количества инфраструктуры.
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public class MainForm : Form
{
private TypeWithAsync _type;
[STAThread()]
public static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MainForm());
}
public MainForm()
{
_type = new TypeWithAsync();
_type.DoSomethingCompleted += DoSomethingCompleted;
var panel = new FlowLayoutPanel() { Dock = DockStyle.Fill };
var btn = new Button() { Text = "Synchronous" };
btn.Click += SyncClick;
panel.Controls.Add(btn);
btn = new Button { Text = "Asynchronous" };
btn.Click += AsyncClick;
panel.Controls.Add(btn);
Controls.Add(panel);
}
private void SyncClick(object sender, EventArgs e)
{
int value = _type.DoSomething();
MessageBox.Show(string.Format("DoSomething() returned {0}.", value));
}
private void AsyncClick(object sender, EventArgs e)
{
_type.DoSomethingAsync();
}
private void DoSomethingCompleted(object sender, DoSomethingCompletedEventArgs e)
{
MessageBox.Show(string.Format("DoSomethingAsync() returned {0}.", e.Value));
}
}
class TypeWithAsync
{
private AsyncOperation _operation;
// synchronous version of method
public int DoSomething()
{
Thread.Sleep(5000);
return 27;
}
// async version of method
public void DoSomethingAsync()
{
if (_operation != null)
{
throw new InvalidOperationException("An async operation is already running.");
}
_operation = AsyncOperationManager.CreateOperation(null);
ThreadPool.QueueUserWorkItem(DoSomethingAsyncCore);
}
// wrapper used by async method to call sync version of method, matches WaitCallback so it
// can be queued by the thread pool
private void DoSomethingAsyncCore(object state)
{
int returnValue = DoSomething();
var e = new DoSomethingCompletedEventArgs(returnValue);
_operation.PostOperationCompleted(RaiseDoSomethingCompleted, e);
}
// wrapper used so async method can raise the event; matches SendOrPostCallback
private void RaiseDoSomethingCompleted(object args)
{
OnDoSomethingCompleted((DoSomethingCompletedEventArgs)args);
}
private void OnDoSomethingCompleted(DoSomethingCompletedEventArgs e)
{
var handler = DoSomethingCompleted;
if (handler != null) { handler(this, e); }
}
public EventHandler<DoSomethingCompletedEventArgs> DoSomethingCompleted;
}
public class DoSomethingCompletedEventArgs : EventArgs
{
private int _value;
public DoSomethingCompletedEventArgs(int value)
: base()
{
_value = value;
}
public int Value
{
get { return _value; }
}
}
}
Во многих простых случаях можно использовать делегата MethodInvoker и избежать потребности создать собственный тип делегата.