引发事件时,事件处理程序始终为null



我正在编写一个类库,该类库连接到Twitter Streaming API并实时处理连续的JSON流。每当从API接收到新的tweet时,我想引发一个事件,这样我就可以在调用程序类中使用lambda方法,如:

var stream = new Stream(customerKey,customerSecret,accessToken,accessTokenSecret);
stream.Start();
// Handler
stream.TweetReceivedEvent += (sender, tweetargs) =>
{
Console.WriteLine(tweetargs.Tweet.ToString());
};

然而,我不确定如何做到这一点。目前,我已经创建了一个名为Stream的类,其中包含与连接到Twitter Streaming API相关的逻辑(为简洁起见,仅包含下面这个类中的Start()方法):

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);
}

在同一个类中,我创建了一个事件和一个类似的委托:

public event TweetReceivedHandler TweetReceivedEvent;
public delegate void TweetReceivedHandler(TwitterStreamClient s, TweetEventArgs e);

Raise方法调用EventHandler方法,该方法在我的测试程序类中具有特色

public void Raise(TweetReceivedHandler handler, TweetEventArgs e)
{
if (handler != null)
{
handler(this, e);
}
}

但是,当我调试并逐步执行Raise方法时,Handler始终为null。我在这里错过了什么?正如你所看到的,我已经采取了一些步骤使这个方法异步并返回一个Task,尽管我不确定这是否是正确的操作过程。

如果你需要任何澄清,请随时询问,如果你能解释我需要做什么,我将永远感激你!如果我完全搞错了,请提前道歉!

该类的一个(稍旧的)版本存在于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()方法:获取请求流、写入请求流、获取响应和处理响应流。通过这种方式,方法的逻辑可以以直接、循序渐进的方式编写,但仍允许异步操作。也就是说,当这些异步操作正在进行时,该方法将返回并且当前线程的执行可以继续,并且该方法将在稍后这些操作完成时恢复执行。

最重要的是,这样做可以确保该方法返回的Task对象在整个操作完成之前不会完成。通过这种方式,您实际上可以完全删除事件,并依靠Task对象本身来发出完成信号。然后你的呼叫站点看起来是这样的:

var stream = new Stream(customerKey,customerSecret,accessToken,accessTokenSecret);
await stream.Start();
Console.WriteLine(tweetargs.Tweet.ToString());


最后,还有一个建议:我强烈建议您在类中使用Stream以外的名称。在试图读取和维护代码时,名称Stream实际上保证会引起混淆。

相关内容

  • 没有找到相关文章

最新更新