3

URLからHTMLを取得し、それを解析してエンティティを抽出し、エンティティのリストを返すメソッドを持っています。サンプルコードは次のとおりです。

  public List<Entity> FetchEntities()
    {
        List<Entity> myList = new List<Entity>();
        string url = "<myUrl>";
        string response = String.Empty;
        client = new WebClient();
        client.DownloadStringCompleted += (sender, e) =>
        {
            response = e.Result;
            // parse response
            // extract content and generate entities
            // <---- I am currently filling list here
        };
        client.DownloadStringAsync(new Uri(url));

        return myList;
    }

問題は、非同期呼び出しの進行中に制御が empty で返されることですmyList。どうすればこれを防ぐことができますか。私の最終的な目標は、満たされたリストを返すことです。

また、このメソッドは別のクラス ライブラリ プロジェクトにあり、Windows Phone アプリケーションから呼び出されているため、そのようにしておく必要があります。これを行う方法はありますか、それとも何か不足していますか? どんな助けでも大歓迎です。

4

2 に答える 2

4

このようにコールバックをメソッドに渡し、タスクなしで非同期にすることができるため、メソッドの使用法を少し更新する必要があります。

public void FetchEntities(
    Action<List<Entity>> resultCallback, 
    Action<string> errorCallback)
{
    List<Entity> myList = new List<Entity>();
    string url = "<myUrl>";
    string response = String.Empty;
    client = new WebClient();
    client.DownloadStringCompleted += (sender, e) =>
    {
        response = e.Result;
        // parse response
        // extract content and generate entities
        // <---- I am currently filling list here

        if (response == null)
        {
            if (errorCallback != null)
                errorCallback("Ooops, something bad happened");
        }
        else
        {
            if (callback != null)
                callback(myList);
        }
    };
    client.DownloadStringAsync(new Uri(url));
}

もう 1 つのオプションは、同期を強制することです。そのように

public List<Entity> FetchEntities()
{
    List<Entity> myList = new List<Entity>();
    string url = "<myUrl>";
    string response = String.Empty;
    client = new WebClient();
    AutoResetEvent waitHandle = new AutoResetEvent(false);
    client.DownloadStringCompleted += (sender, e) =>
    {
        response = e.Result;
        // parse response
        // extract content and generate entities
        // <---- I am currently filling list here

        waitHandle.Set();
    };
    client.DownloadStringAsync(new Uri(url));

    waitHandle.WaitOne();

    return myList;
}
于 2013-01-12T09:37:42.247 に答える
4

これが、非同期プログラミングがノンブロッキングになるポイントです。コールバックをパラメーターとして渡し、結果を返そうとする代わりに別の場所で処理できます。

結果を返す必要がある場合は、この TPL ライブラリを使用できます。しばらくの間、問題なく使用しています。

public Task<string> GetWebResultAsync(string url)
     {
         var tcs = new TaskCompletionSource<string>();
         var client = new WebClient();

         DownloadStringCompletedEventHandler h = null;
         h = (sender, args) =>
                 {
                     if (args.Cancelled)
                     {
                         tcs.SetCanceled();
                     }
                     else if (args.Error != null)
                     {
                         tcs.SetException(args.Error);
                     }
                     else
                     {
                         tcs.SetResult(args.Result);
                     }

                     client.DownloadStringCompleted -= h;
                 };

         client.DownloadStringCompleted += h;
         client.DownloadStringAsync(new Uri(url));

         return tcs.Task;
     }
}

そして、それを呼び出すことは、.net 4.0 で TPL を使用する方法とまったく同じです。

GetWebResultAsnyc(url).ContinueWith((t) => 
                                    {
                                         t.Result //this is the downloaded string
                                    });

また:

var downloadTask = GetWebResultAsync(url);
downloadTask.Wait();
var result = downloadTask.Result; //this is the downloaded string

お役に立てれば :)

于 2013-01-12T07:08:06.840 に答える