引发事件时,事件处理程序始终为 null
Event handler is always null when raising event
我正在编写一个 class 库,它连接到 Twitter Streaming API 并实时处理连续的 JSON 流。每次从 API 收到一条新推文时,我都想引发一个事件,这样我就可以在调用者 class 中使用 lambda 方法,如下所示:
var stream = new Stream(customerKey,customerSecret,accessToken,accessTokenSecret);
stream.Start();
// Handler
stream.TweetReceivedEvent += (sender, tweetargs) =>
{
Console.WriteLine(tweetargs.Tweet.ToString());
};
但是,我不确定该怎么做。目前,我创建了一个名为 Stream
的 class,其中包含与连接到 Twitter Streaming API 相关的逻辑(仅包含此 [= 中的 Start()
方法36=] 下面为简洁起见):
public async Task Start()
{
//Twitter Streaming API
string stream_url = "https://stream.twitter.com/1.1/statuses/filter.json";
string trackKeywords = "twitter";
string followUserId = "";
string locationCoord = "";
string postparameters = (trackKeywords.Length == 0 ? string.Empty : "&track=" + trackKeywords) +
(followUserId.Length == 0 ? string.Empty : "&follow=" + followUserId) +
(locationCoord.Length == 0 ? string.Empty : "&locations=" + locationCoord);
if (!string.IsNullOrEmpty(postparameters))
{
if (postparameters.IndexOf('&') == 0)
postparameters = postparameters.Remove(0, 1).Replace("#", "%23");
}
//Connect
webRequest = (HttpWebRequest) WebRequest.Create(stream_url);
webRequest.Timeout = -1;
webRequest.Headers.Add("Authorization", GetAuthHeader(stream_url + "?" + postparameters));
Encoding encode = Encoding.GetEncoding("utf-8");
if (postparameters.Length > 0)
{
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
byte[] _twitterTrack = encode.GetBytes(postparameters);
webRequest.ContentLength = _twitterTrack.Length;
var _twitterPost = webRequest.GetRequestStream();
_twitterPost.Write(_twitterTrack, 0, _twitterTrack.Length);
_twitterPost.Close();
}
webRequest.BeginGetResponse(ar =>
{
var req = (WebRequest)ar.AsyncState;
using (var response = req.EndGetResponse(ar))
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
while (!reader.EndOfStream)
{
// Deserialize the JSON obj to type Tweet
var jsonObj = JsonConvert.DeserializeObject<Tweet>(reader.ReadLine(), new JsonSerializerSettings());
Console.WriteLine(jsonObj.Text);
Raise(TweetReceivedEvent, new TweetReceivedEventArgs(jsonObj));
}
}
}
}, webRequest);
}
在同一个 class 中,我创建了一个事件和一个委托,如下所示:
public event TweetReceivedHandler TweetReceivedEvent;
public delegate void TweetReceivedHandler(TwitterStreamClient s, TweetEventArgs e);
并且 Raise
方法调用我的测试器中的 EventHandler 方法 class:
public void Raise(TweetReceivedHandler handler, TweetEventArgs e)
{
if (handler != null)
{
handler(this, e);
}
}
然而,当我调试并单步执行 Raise
方法时,处理程序始终为空。我在这里错过了什么?如您所见,我已经采取了一些步骤来使此方法异步并返回一个任务,尽管我不确定这是正确的操作过程。
如果您需要任何说明,请随时提出,如果您能解释我需要做什么,我将永远感激您!如果我完全理解错误的一端,请提前道歉!
如果您想查看完整代码,class 有一个(稍旧的)版本 https://github.com/adaam2/APoorMansTwitterStreamingClient/blob/master/TwitterClient/Infrastructure/Utility/TwitterStreamClient.cs。
正如 Hans 所建议的,查看引发的事件的关键是确保在可以引发事件之前订阅它。否则,它可能会在您有机会订阅它之前被筹集到。例如:
var stream = new Stream(customerKey,customerSecret,accessToken,accessTokenSecret);
// Handler
stream.TweetReceivedEvent += (sender, tweetargs) =>
{
Console.WriteLine(tweetargs.Tweet.ToString());
};
stream.Start();
就修复 Start()
方法而言,编写 async
方法的关键是确保在其中使用 await
。理想情况下,方法中启动的 所有 异步操作都是可等待的,这样您就可以在整个过程中使用简化的 await
语法。
在你的例子中,我会推荐更像这样的东西:
public async Task Start()
{
//Twitter Streaming API
string stream_url = "https://stream.twitter.com/1.1/statuses/filter.json";
string trackKeywords = "twitter";
string followUserId = "";
string locationCoord = "";
string postparameters = (trackKeywords.Length == 0 ? string.Empty : "&track=" + trackKeywords) +
(followUserId.Length == 0 ? string.Empty : "&follow=" + followUserId) +
(locationCoord.Length == 0 ? string.Empty : "&locations=" + locationCoord);
if (!string.IsNullOrEmpty(postparameters))
{
if (postparameters.IndexOf('&') == 0)
postparameters = postparameters.Remove(0, 1).Replace("#", "%23");
}
//Connect
webRequest = (HttpWebRequest) WebRequest.Create(stream_url);
webRequest.Timeout = -1;
webRequest.Headers.Add("Authorization", GetAuthHeader(stream_url + "?" + postparameters));
Encoding encode = Encoding.GetEncoding("utf-8");
if (postparameters.Length > 0)
{
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
byte[] _twitterTrack = encode.GetBytes(postparameters);
webRequest.ContentLength = _twitterTrack.Length;
var _twitterPost = await webRequest.GetRequestStreamAsync();
await _twitterPost.WriteAsync(_twitterTrack, 0, _twitterTrack.Length);
_twitterPost.Close();
}
using (var response = await webRequest.GetResponseAsync())
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
while (!reader.EndOfStream)
{
// Deserialize the JSON obj to type Tweet
var jsonObj = JsonConvert.DeserializeObject<Tweet>(await reader.ReadLineAsync(), new JsonSerializerSettings());
Console.WriteLine(jsonObj.Text);
Raise(TweetReceivedEvent, new TweetReceivedEventArgs(jsonObj));
}
}
}
}
注意,我在上面四个地方使用了...Async()
方法:获取请求流、写入请求流、获取响应、处理响应流。通过这种方式,可以直接、逐步地编写方法的逻辑,同时仍然允许异步操作。也就是说,该方法将 return 并且在这些异步操作正在进行时可以继续执行当前线程,并且该方法将在它们完成后恢复执行。
最重要的是,这样做可以确保 Task
由该方法编辑的对象 return 在整个操作完成之前不会自行完成。这样,您实际上可以 完全删除 事件并依靠 Task
对象本身来发出完成信号。然后你的呼叫站点看起来像这样:
var stream = new Stream(customerKey,customerSecret,accessToken,accessTokenSecret);
await stream.Start();
Console.WriteLine(tweetargs.Tweet.ToString());
最后,还有一个建议:我强烈建议您使用 Stream
以外的名称作为您的 class。在尝试阅读和维护代码时,名称 Stream
实际上肯定会造成混淆。
我正在编写一个 class 库,它连接到 Twitter Streaming API 并实时处理连续的 JSON 流。每次从 API 收到一条新推文时,我都想引发一个事件,这样我就可以在调用者 class 中使用 lambda 方法,如下所示:
var stream = new Stream(customerKey,customerSecret,accessToken,accessTokenSecret);
stream.Start();
// Handler
stream.TweetReceivedEvent += (sender, tweetargs) =>
{
Console.WriteLine(tweetargs.Tweet.ToString());
};
但是,我不确定该怎么做。目前,我创建了一个名为 Stream
的 class,其中包含与连接到 Twitter Streaming API 相关的逻辑(仅包含此 [= 中的 Start()
方法36=] 下面为简洁起见):
public async Task Start()
{
//Twitter Streaming API
string stream_url = "https://stream.twitter.com/1.1/statuses/filter.json";
string trackKeywords = "twitter";
string followUserId = "";
string locationCoord = "";
string postparameters = (trackKeywords.Length == 0 ? string.Empty : "&track=" + trackKeywords) +
(followUserId.Length == 0 ? string.Empty : "&follow=" + followUserId) +
(locationCoord.Length == 0 ? string.Empty : "&locations=" + locationCoord);
if (!string.IsNullOrEmpty(postparameters))
{
if (postparameters.IndexOf('&') == 0)
postparameters = postparameters.Remove(0, 1).Replace("#", "%23");
}
//Connect
webRequest = (HttpWebRequest) WebRequest.Create(stream_url);
webRequest.Timeout = -1;
webRequest.Headers.Add("Authorization", GetAuthHeader(stream_url + "?" + postparameters));
Encoding encode = Encoding.GetEncoding("utf-8");
if (postparameters.Length > 0)
{
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
byte[] _twitterTrack = encode.GetBytes(postparameters);
webRequest.ContentLength = _twitterTrack.Length;
var _twitterPost = webRequest.GetRequestStream();
_twitterPost.Write(_twitterTrack, 0, _twitterTrack.Length);
_twitterPost.Close();
}
webRequest.BeginGetResponse(ar =>
{
var req = (WebRequest)ar.AsyncState;
using (var response = req.EndGetResponse(ar))
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
while (!reader.EndOfStream)
{
// Deserialize the JSON obj to type Tweet
var jsonObj = JsonConvert.DeserializeObject<Tweet>(reader.ReadLine(), new JsonSerializerSettings());
Console.WriteLine(jsonObj.Text);
Raise(TweetReceivedEvent, new TweetReceivedEventArgs(jsonObj));
}
}
}
}, webRequest);
}
在同一个 class 中,我创建了一个事件和一个委托,如下所示:
public event TweetReceivedHandler TweetReceivedEvent;
public delegate void TweetReceivedHandler(TwitterStreamClient s, TweetEventArgs e);
并且 Raise
方法调用我的测试器中的 EventHandler 方法 class:
public void Raise(TweetReceivedHandler handler, TweetEventArgs e)
{
if (handler != null)
{
handler(this, e);
}
}
然而,当我调试并单步执行 Raise
方法时,处理程序始终为空。我在这里错过了什么?如您所见,我已经采取了一些步骤来使此方法异步并返回一个任务,尽管我不确定这是正确的操作过程。
如果您需要任何说明,请随时提出,如果您能解释我需要做什么,我将永远感激您!如果我完全理解错误的一端,请提前道歉!
如果您想查看完整代码,class 有一个(稍旧的)版本 https://github.com/adaam2/APoorMansTwitterStreamingClient/blob/master/TwitterClient/Infrastructure/Utility/TwitterStreamClient.cs。
正如 Hans 所建议的,查看引发的事件的关键是确保在可以引发事件之前订阅它。否则,它可能会在您有机会订阅它之前被筹集到。例如:
var stream = new Stream(customerKey,customerSecret,accessToken,accessTokenSecret);
// Handler
stream.TweetReceivedEvent += (sender, tweetargs) =>
{
Console.WriteLine(tweetargs.Tweet.ToString());
};
stream.Start();
就修复 Start()
方法而言,编写 async
方法的关键是确保在其中使用 await
。理想情况下,方法中启动的 所有 异步操作都是可等待的,这样您就可以在整个过程中使用简化的 await
语法。
在你的例子中,我会推荐更像这样的东西:
public async Task Start()
{
//Twitter Streaming API
string stream_url = "https://stream.twitter.com/1.1/statuses/filter.json";
string trackKeywords = "twitter";
string followUserId = "";
string locationCoord = "";
string postparameters = (trackKeywords.Length == 0 ? string.Empty : "&track=" + trackKeywords) +
(followUserId.Length == 0 ? string.Empty : "&follow=" + followUserId) +
(locationCoord.Length == 0 ? string.Empty : "&locations=" + locationCoord);
if (!string.IsNullOrEmpty(postparameters))
{
if (postparameters.IndexOf('&') == 0)
postparameters = postparameters.Remove(0, 1).Replace("#", "%23");
}
//Connect
webRequest = (HttpWebRequest) WebRequest.Create(stream_url);
webRequest.Timeout = -1;
webRequest.Headers.Add("Authorization", GetAuthHeader(stream_url + "?" + postparameters));
Encoding encode = Encoding.GetEncoding("utf-8");
if (postparameters.Length > 0)
{
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
byte[] _twitterTrack = encode.GetBytes(postparameters);
webRequest.ContentLength = _twitterTrack.Length;
var _twitterPost = await webRequest.GetRequestStreamAsync();
await _twitterPost.WriteAsync(_twitterTrack, 0, _twitterTrack.Length);
_twitterPost.Close();
}
using (var response = await webRequest.GetResponseAsync())
{
using (var reader = new StreamReader(response.GetResponseStream()))
{
while (!reader.EndOfStream)
{
// Deserialize the JSON obj to type Tweet
var jsonObj = JsonConvert.DeserializeObject<Tweet>(await reader.ReadLineAsync(), new JsonSerializerSettings());
Console.WriteLine(jsonObj.Text);
Raise(TweetReceivedEvent, new TweetReceivedEventArgs(jsonObj));
}
}
}
}
注意,我在上面四个地方使用了...Async()
方法:获取请求流、写入请求流、获取响应、处理响应流。通过这种方式,可以直接、逐步地编写方法的逻辑,同时仍然允许异步操作。也就是说,该方法将 return 并且在这些异步操作正在进行时可以继续执行当前线程,并且该方法将在它们完成后恢复执行。
最重要的是,这样做可以确保 Task
由该方法编辑的对象 return 在整个操作完成之前不会自行完成。这样,您实际上可以 完全删除 事件并依靠 Task
对象本身来发出完成信号。然后你的呼叫站点看起来像这样:
var stream = new Stream(customerKey,customerSecret,accessToken,accessTokenSecret);
await stream.Start();
Console.WriteLine(tweetargs.Tweet.ToString());
最后,还有一个建议:我强烈建议您使用 Stream
以外的名称作为您的 class。在尝试阅读和维护代码时,名称 Stream
实际上肯定会造成混淆。