我如何编写像WebClient这样的异步类?
无论如何,我可以保持简短,不必为每种方法重复它吗?
例如,我有:
Download(string a)
Download(string a, string b)
我是否必须为每个方法重写Async + Complete方法?
非常感谢。
答案 0 :(得分:3)
这个例子适用于winforms,我相信OP正在询问asp.net。一旦我从评论中得到一些反馈,我就会修改我的答案。
MSDN页面上的示例说实话可能有点压倒性,你可以这样总结(伪代码 - 可能无法编译):
private BackgroundWorker bw;
private void foobar() {
bw = new BackgroundWorker(); // should be called once, in ctor
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_Completed);
int i = 0;
bw.RunWorkerAsync(i);
}
private void bw_DoWork(object sender, DoWorkEventArgs e) {
int i = (int)e.Argument;
i++;
e.Result = i;
}
private void bw_Completed(object sender, RunWorkerCompletedEventArgs e) {
if (e.Error != null) {
MessageBox.Show(e.Error.Message);
} else {
int i = (int)e.Result;
MessageBox.Show(i.ToString());
}
}
至于将重复的代码保持在最低限度,你可以使用相同的后台工作程序来实现我想要的所有方法,但是你可能正在使用一个闪亮的金色锤子进入臭代码领域。只提供一些方法,然后为每个操作创建事件处理程序不应该看起来太乱。
答案 1 :(得分:1)
如果要异步调用同步方法,则需要使用委托。 http://msdn.microsoft.com/en-us/library/2e08f6yc.aspx。使用这种方法,您不必创建Async和Complete方法。
您描述的模式是使用IAsyncResult,在这种情况下您需要实现IAsyncResult(您也可以根据您的要求使用Framework中的那个)。在您的课程中导出并实现您提到的Async和Complete方法。
答案 2 :(得分:1)
听起来您正在询问实现类的最佳实践,该类公开了具有类似于WebClient的异步版本的方法?
值得注意的是,.NET框架中通常使用两种模式来支持异步使用。例如,您通常会看到像Stream这样的类公开BeginRead和EndRead。这些使用IAsyncResult并且使用起来往往更复杂。 (Overview on MSDN)
然后他们提出了event-based async pattern,例如WebClient使用的那个,你有一个DownloadString / DownloadStringAsync方法和一个相应的DownloadStringCompleted事件。对于简单的情况,这更容易使用,但在我看来,它不像开始/结束模式那样灵活。
因此,在您的示例中,您有两个重载方法。在这种情况下,我只是设计它们,使得参数较少的方法遵循方法,更多参数根据需要传递默认值或空值。你可以有多个Download / DownloadAsync重载但你只能有一个DownloadCompleted事件。
这是一个非常基本的实现。请注意,实际执行任何工作的唯一方法是一个同步下载方法。同样重要的是要注意,这也不是最有效的方法。如果您想利用HttpWebRequest的异步IO功能等,这个例子会很快变得复杂。还有一个过于复杂的Async Operation模式,我从来都不喜欢它。
class Downloader {
public void Download(string url, string localPath) {
if (localPath == null) {
localPath = Environment.CurrentDirectory;
}
// implement blocking download code
}
public void Download(string url) {
Download(url, null);
}
public void DownloadAsync(string url, string localPath) {
ThreadPool.QueueUserWorkItem( state => {
// call the sync version using your favorite
// threading api (threadpool, tasks, delegate.begininvoke, etc)
Download(url, localPath);
// synchronizationcontext lets us raise the event back on
// the UI thread without worrying about winforms vs wpf, etc
SynchronizationContext.Current.Post( OnDownloadCompleted, null );
});
}
public void DownloadAsync(string url) {
DownloadAsync(url, null);
}
private void OnDownloadCompleted(object state) {
var handler = DownloadCompleted;
if (handler != null) {
handler(this, EventArgs.Empty);
}
}
public event EventHandler DownloadCompleted;
}
答案 3 :(得分:0)
使用MethodInvoker.BeginInvoke调用新的thead。