在Silverlight WP7中模拟同步调用

8

我正在将一些代码从完整的.NET框架移植到WP7版本,并且在同步和异步调用之间遇到了问题。

 string response;
 string requestString = GenerateReqString();
 HttpWebRequest req = (HttpWebRequest) WebRequest.Create("endpoint");
 req.Method = "POST";
 req.ContentType = "text/xml";

 req.ContentLength = requestString.Length;

 StreamWriter sw = new StreamWriter (req.GetRequestStream(), System.Text.Encoding.ASCII);
 sw.Write(requestString);
 sw.Close();

 StreamReader sr = new StreamReader(req.GetResponse().GetResponseStream());
 response = sr.ReadToEnd();
 sr.Close();

然后将响应字符串解析成对象列表,并由该方法返回。

我的问题是,在Silverlight/WP7中没有同步调用的方法。如果我使用回调,我将在不同的函数中获得响应,并且将无法从原始函数返回它。是否有一种方法可以使调用同步或从CallBack函数返回到启动异步调用的方法?

4个回答

11
你需要从不同的角度思考问题。要使异步操作感觉像同步操作,最简单的方法是重构你的代码,利用“继续传递风格” 。
实质上,你需要调用一个函数,并将一个匿名函数作为代理传递给它,而不是调用返回值的函数,然后处理该值。被调用的函数将调用代理,并传入字符串。
以下是一个使用匿名函数和 lambda 的示例:
void DoSomethingAsync( Action<string> callback ) {
    HttpWebRequest req; // TODO: build your request

    req.BeginGetResponse( result => {
        // This anonymous function is a closure and has access 
        // to the containing (or enclosing) function.
        var response = req.EndGetResponse( result );

        // Get the result string and call the callback
        string resultString = null; // TODO: read from the stream

        callback(resultString);
    }, null );
}

这是解决方案的一半。下一步是实际调用它。想象一下,您有一个ICommand实例或更简单的按钮点击事件需要调用此函数并“获取字符串”。 您可以调用此函数并提供回调方法(将是闭包),而不是“获取字符串”。

void btnGo_Click( object sender, EventArgs e ) {
    DoSomethingAsync( resultString => {
        // This anonymous function is called when the web request has
        // finished and has your string. 

        // Now that we have the string, we can go and process it.
        ProcessWebResponseResult( resultString );
    });
}

这里有一篇很好的文章进一步解释这个概念:http://blogs.msdn.com/b/wesdyer/archive/2007/12/22/continuation-passing-style.aspx


假设ProcessWebResponseResult()从字符串创建一个对象,调用DoSomethingAsync()的函数需要返回该对象。这是否可能? - CACuzcatlan
那是错失重点了。调用DoSomethingAsync的方法向其传递一个函数。传递的函数是“如何处理此函数的结果”。如果您需要更多,则将其他续继函数添加到调用链中。 - Adam Sills
这是正常思考程序运行的方式:A();B();C();。然而,当您使用'延续传递样式'执行时,您告诉每个函数在完成时要做什么。您不是说“我从这个函数得到我的结果”,而是通过委托说“我告诉这个函数如何处理它的结果”。 - Adam Sills
也许Don Box在这个问题上的讲解可以帮到你:http://www.pluralsight-training.net/community/blogs/dbox/archive/2005/04/27/7780.aspx - Adam Sills
已更新答案,并附上了一个很好的解释链接。 - Adam Sills

2

首先,我建议尝试适应异步操作,但如果您真的想/需要将异步调用转换为同步调用,您可以使用ManualResetEvent来实现所需的结果。

这里是一个快速示例:

public ManualResetEvent _event = new ManualResetEvent(false);

...
{
    ...
    var wc = new WebClient();
    wc.OpenReadCompleted += new OpenReadCompletedEventHandler(ReadCompleted);
    wc.OpenReadAsync(uri);

    // block until async call is complete
    _event.WaitOne();
}

private static void ReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
    ...
    // set event to unblock caller
    _event.Set();
}

现在你的代码将会阻塞在 _event.WaitOne(); 这一行,直到调用了 _event.Set();

祝好运!


1

-2

最好异步完成,这样用户可以继续与设备交互。

我猜想你想要这样做的原因是为了防止用户在你的请求完成前与某些控件进行交互。

处理请求期间不应进行交互的 UI 元素通常会被隐藏或禁用,这是一种被广泛认可的方法。


最好将所有这些抽象化,并从更高层次开始异步执行,此时,您希望在某个工作线程上同步执行低级请求。 - Joel Day

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接