我正在学习创建事件和创建多线程应用程序。
方法Thread
由另一个类调用,该类用搜索条件填充参数。创建BackgroundWorker
,执行搜索并将结果返回到worker_RunWorkerCompleted
。
在worker_RunWorkerCompleted
内,我想将结果发送回订阅Fireendofsearch
事件的用户界面。
我无法理解为什么我的代码会抛出以下错误
对象引用未设置为对象的实例。
当我触发事件Fireendofsearch
public class BackgroundSearch
{
public event SearchResultCompleteThreaded Fireendofsearch;
public EventArgs a = null;
public delegate void SearchResultCompleteThreaded(object seachresults, EventArgs a);
internal void Thread(string folder, string parms)
{
var Argument = new List<object> { folder, parms };
var worker = new BackgroundWorker {WorkerReportsProgress = false, WorkerSupportsCancellation = false};
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.RunWorkerAsync(Argument);
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
var passedAugue = e.Argument as List<object>;
var returnResult = new List<string[]>();
if (passedAugue != null)
{
var result = Directory.GetFiles(passedAugue[0].ToString(), passedAugue[1].ToString(), SearchOption.AllDirectories);
foreach (string s in result)
{
var t = new string[4];
t[0] = s;
t[1] = File.GetCreationTime(s).ToString();
t[2] = File.GetLastAccessTime(s).ToString();
t[3] = File.GetLastWriteTime(s).ToString();
returnResult.Add(t);
}
}
if (returnResult.Count != 0)
{
e.Result = returnResult;
}
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Result != null)
{
Fireendofsearch(e.Result, a);
}
}
}
答案 0 :(得分:2)
Firendofsearch将为null,直到有人订阅它为止,将工作完成的事件处理程序更改为此以修复它。
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Result != null)
{
var friendOfSearch = Fireendofsearch;
if(friendOfSearch != null)
friendOfSearch (e.Result, a);
}
}
我将它复制到变量的原因是,如果另一个线程中的某个人是在null检查和引发事件之间取消订阅的最后一个人,那么你仍将得到空引用异常,通过首先解决另一个变量那个问题。
但是如果我在编写它的地方,我会做一些其他更改,由于某种原因你重新调整null EventArgs
并将结果作为传统事件模式中的“Sender”传回。我会将您的代码更改为此
public event EventHandler<FriendOfSearchArgs> FirendsOfSearch;
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Result != null)
{
RaiseFriendOfSearch(e.Result);
}
}
protected virtual void RaiseFriendOfSearch(object result)
{
var friendOfSearch = FirendsOfSearch;
if(friendOfSearch != null)
friendOfSearch(this, new FriendOfSearchArgs(result));
}
public class FriendOfSearchArgs : EventArgs
{
public FriendOfSearchArgs(object result)
{
Result = result;
}
public object Result {get; private set;}
}
这些都写在SO文本框中,因此可能会有一两个错误。
答案 1 :(得分:0)
出于某种原因(最有可能是优化),只有在第一个处理程序方法订阅它时才会实例化事件。
您必须在代码中检查该内容。
以下是我通常宣布一个事件的方式:
public event SearchResultCompleteThreaded Fireendofsearch;
private void RaiseFireEndOfSearchEvent(EventArgs e)
{
if (Fireendofsearch != null)
{
Fireendofsearch(this, e);
}
}
每当我需要引发事件时,我只会调用辅助方法。
答案 2 :(得分:0)
在尝试调用委托之前,应该检查null。并且您必须首先将其拉入单独的变量以避免线程问题。
var ev = Fireendofsearch;
if ( ev != null ) ev( ... );
我还发现在这种情况下有一个扩展方法很有用:
public static void Raise ( this EventHandler h, object sender )
{
if ( h != null) h( sender, EventArgs.Empty );
}
然后:
MyEvent.Raise ( this );
答案 3 :(得分:0)
“在你的公共事件后面”,有一个SearchResultCompleteThreaded
类型的隐式私有变量。类型SearchResultCompleteThreaded
是委托类型。
在.NET中,所有代表都是“多播”代理。这意味着他们有一个调用列表(GetInvocationList()
上的SearchResultCompleteThreaded
方法来自System.Delegate.GetInvocationList()
。
现在,在.NET中,调用列表保证包含一个或更多项(不零或更多)。任何委托类型都是不可变类型。但是,如果您尝试通过“减去”现有实例的调用列表中的所有成员来创建新实例,如var newDel = oldDel - oldDel;
或reuseDel -= reuseDel;
,那么不要获取具有零的新实例 - 长度调用列表,你得到一个空引用!
关于这一点的好处是,您不必担心“空”委托实例(否则可能被允许)与空引用之间的细微差别。关于它的坏处是你上面遇到的问题。
答案 4 :(得分:-1)
集:
public event SearchResultCompleteThreaded Fireendofsearch = delegate { };
可能需要初始化吗?