簡體   English   中英

如何在具有返回類型的方法中調用異步方法?

[英]How to call async method inside a method which has return type?

這是Windows Phone 8.1 Silverlight應用程序。 我有一個文件關聯。 為此,我有一堂課

class AssociationUriMapper : UriMapperBase
{
    public override Uri MapUri(Uri uri)
    {
       //here I'm getting file ID etc..
    }

    // here I want to read the file content & determine the file type because,
    // the case is, even same file extension can contain different type of data

    switch (fileType)
    {
       //here I'm calling appropriate page according to type
    }
}

現在的問題是MapUri被重寫方法,因此它必須具有返回類型。 而OpenStreamForReadAsync()是一個異步方法。 我嘗試了Wait()方法,創建新任務,然后在其中調用Start(),Wait(),但沒有成功。 目前我的代碼是

class AssociationUriMapper : UriMapperBase
{
    string strData = "";
    public override Uri MapUri(Uri uri)
    {
        strUri = uri.ToString();

        // File association launch
        if (strUri.Contains("/FileTypeAssociation"))
        {
            // Get the file ID (after "fileToken=").
            int nFileIDIndex = strUri.IndexOf("fileToken=") + 10;
            string strFileID = strUri.Substring(nFileIDIndex);

            string strFileName = SharedStorageAccessManager.GetSharedFileName(strFileID);
            string strIncomingFileType = Path.GetExtension(strFileName);

            fnCopyToLocalFolderAndReadContents(strFileID);

            switch (fileType)
            {
                case ".gmm":
                       //determine if gmm is text
                       if (objGMM.fnGetGMMType() == GMMFILETYPE.TXT)
                       {
                           return new Uri("/PageReadText.xaml?data=" + strData, UriKind.Relative);
                       }
                       break;
             }
        }
  }

  async void fnCopyToLocalFolderAndReadContents(string strIncomingFileId)
  {
     StorageFolder objLocalFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
     objFile = await SharedStorageAccessManager.CopySharedFileAsync(objLocalFolder, TEMP.gmm, NameCollisionOption.ReplaceExisting, strIncomingFileId);

     using (StreamReader streamReader = new StreamReader(objFile))
     {
        strData = streamReader.ReadToEnd();
     }
  }
}

我要做的第一件事就是改變邏輯。 當操作系統詢問您的應用程序是否支持Uri映射時,它會立即得到答復。 它不希望該應用程序復制和讀取文件。 通常,Uri映射非常恆定。 一個應用程序總是支持一個,或者不支持。

因此,我要做的第一件事是在啟動時加載所有映射文件, 然后使用所有結果創建AssociationUriMapper 如果這不可能,那么幾乎可以肯定的是,您將Uri映射用於錯誤的事情。 它們不應該是動態的,操作系統很有可能會認為它們不是動態的。

就是說,如果您想使其正常運行,我認為最干凈的解決方案是將異步文件操作推送到另一個線程,然后在該線程上進行阻塞:

public override Uri MapUri(Uri uri)
{
  strUri = uri.ToString();

  // File association launch
  if (strUri.Contains("/FileTypeAssociation"))
  {
    // Get the file ID (after "fileToken=").
    int nFileIDIndex = strUri.IndexOf("fileToken=") + 10;
    string strFileID = strUri.Substring(nFileIDIndex);

    string strFileName = SharedStorageAccessManager.GetSharedFileName(strFileID);
    string strIncomingFileType = Path.GetExtension(strFileName);

    var strData = Task.Run(() => CopyToLocalFolderAndReadContents(strFileID)).GetAwaiter().GetResult();

    switch (fileType)
    {
      case ".gmm":
        //determine if gmm is text
        if (objGMM.fnGetGMMType() == GMMFILETYPE.TXT)
        {
          return new Uri("/PageReadText.xaml?data=" + strData, UriKind.Relative);
        }
        break;
    }
  }
}

async Task<string> CopyToLocalFolderAndReadContentsAsync(string strIncomingFileId)
{
  StorageFolder objLocalFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
  objFile = await SharedStorageAccessManager.CopySharedFileAsync(objLocalFolder, TEMP.gmm, NameCollisionOption.ReplaceExisting, strIncomingFileId);

  using (StreamReader streamReader = new StreamReader(objFile))
  {
    return streamReader.ReadToEnd();
  }
}

我不太喜歡它,因為它涉及同步調用異步方法的代碼。 但是以下應該起作用:

class AssociationUriMapper : UriMapperBase
{
    public override Uri MapUri(Uri uri)
    {
        strUri = uri.ToString();

        // File association launch
        if (strUri.Contains("/FileTypeAssociation"))
        {
            // Get the file ID (after "fileToken=").
            int nFileIDIndex = strUri.IndexOf("fileToken=") + 10;
            string strFileID = strUri.Substring(nFileIDIndex);

            string strFileName = SharedStorageAccessManager.GetSharedFileName(strFileID);
            string strIncomingFileType = Path.GetExtension(strFileName);

            string strData = fnCopyToLocalFolderAndReadContents(strFileID).Result;

            switch (fileType)
            {
                case ".gmm":
                       //determine if gmm is text
                       if (objGMM.fnGetGMMType() == GMMFILETYPE.TXT)
                       {
                           return new Uri("/PageReadText.xaml?data=" + strData, UriKind.Relative);
                       }
                       break;
             }
        }
  }

  async Task<string> fnCopyToLocalFolderAndReadContents(string strIncomingFileId)
  {
     StorageFolder objLocalFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
     objFile = await SharedStorageAccessManager.CopySharedFileAsync(objLocalFolder, TEMP.gmm, NameCollisionOption.ReplaceExisting, strIncomingFileId).ConfigureAwait(false);

     using (StreamReader streamReader = new StreamReader(objFile))
     {
        return streamReader.ReadToEnd();
     }
  }
}

對我來說,一個更大的問題是,為什么要實現一個像MapUri()這樣的方法,使得它需要調用異步方法,並涉及這種可能耗時的I / O。 我的意思是,也許實際上這是必需的,但這似乎有些偏離。 不幸的是,這個問題沒有足夠的上下文讓我覺得我可以提供其他選擇。

不幸的是,沒有覆蓋非異步方法的“漂亮方法”。

您能做的最好的事情就是確保將ConfigureAwait(false)添加到異步調用中,以確保SynchronizationContext不流動和死鎖,然后訪問返回的TaskResult屬性。

我要做的是更改讀取文件的方法以返回Task<string>

async Task<string> CopyToLocalFolderAndReadContents(string strIncomingFileId)
{
    StorageFolder objLocalFolder = Windows.Storage.ApplicationData.Current
                                                                  .LocalFolder;
    objFile = await SharedStorageAccessManager
                   .CopySharedFileAsync(objLocalFolder, TEMP.gmm, 
                                        NameCollisionOption.ReplaceExisting, 
                                        strIncomingFileId)
                   .AsTask().ConfigureAwait(false);

    using (StreamReader streamReader = new StreamReader
          (await objFile.OpenStreamForReadAsync().ConfigureAwait(false)))
    {
        return await streamReader.ReadToEndAsync().ConfigureAwait(false);
    }
}

然后將呼叫站點更改為:

string data = CopyToLocalFolderAndReadContents(strFileID).Result;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM