【问题标题】:How to call async method inside a method which has return type?如何在具有返回类型的方法中调用异步方法?
【发布时间】:2014-11-24 21:22:15
【问题描述】:

这是 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();
     }
  }
}

【问题讨论】:

  • 你说的“没有成功”是什么意思?当你这样做时会发生什么?
  • 这里没有足够的上下文来了解正确的解决方案是什么。您可以更改fnCopyToLocalFolderAndReadContents(),使其返回类型为Task 而不是void。然后你可以在同步的MapUri() 方法中调用Wait()。但这确实是一种次优方法......如果您有异步代码,您应该尝试安排实际利用它。但鉴于您正在覆盖虚拟方法,这将很困难。如果没有更多上下文,很难看出更好的解决方案是什么样的。
  • 也用ReadToEndAsyncReadToEnd是一个阻塞调用。
  • @NedStoyanov 如果他将其标记为async,他必须返回voidTaskTask<T>
  • @YuvalItzchakov:strData 保持为空。有时应用程序会陷入僵局。 @PeterDuniho:试过 Wait().. 没用。

标签: c# silverlight async-await windows-phone-8.1


【解决方案1】:

我要做的第一件事就是改变逻辑。当操作系统询问您的应用程序是否支持 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();
  }
}

【讨论】:

    【解决方案2】:

    我不太喜欢它,因为它涉及到同步调用异步方法的代码。但以下应该有效:

    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。我的意思是,也许这实际上是这里需要的,但它似乎有点不对劲。不幸的是,这个问题没有足够的背景让我觉得我可以提供其他选择。

    【讨论】:

    • 您的代码当前的编写方式,如果从 UI 同步上下文调用它可能会死锁。
    • 是的,没错。这是我不喜欢这种方法的原因之一。是的,我可以打电话给ConfigureAwait(false) 来解决这个问题(也许我应该......我看到你这样做了)。但是恕我直言,最好更好地理解更大的图景并且根本没有这个块。 :(
    • 你是对的,但是当需要重写没有适当异步重载的虚拟方法时,你必须做出这样的变通方法,这是一个已知问题。有时大局是一样的,你只需要那样做。
    • 同意...我已经修复了代码示例。感谢您的意见。
    【解决方案3】:

    不幸的是,没有“漂亮的方式”来覆盖非异步方法。

    您可以做的最好的事情是确保将ConfigureAwait(false) 添加到异步调用中以确保SynchronizationContext 不会流动和死锁,然后访问返回的TaskResult 属性。

    我要做的是更改读取文件的方法以返回Task&lt;string&gt;

    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;
    

    【讨论】:

    • CopySharedFileAsync 语句出错! 'Windows.Foundation.IAsyncOperation&lt;Windows.Storage.IStorageFile&gt;' does not contain a definition for 'ConfigureAwait' and no extension method 'ConfigureAwait' accepting a first argument of type 'Windows.Foundation.IAsyncOperation&lt;Windows.Storage.IStorageFile&gt;' could be found (are you missing a using directive or an assembly reference?)
    • 顺便说一句,只有当我在方法调用之后放置空白while 循环并不断检查标志IsCopyFinished(我在异步方法中制作true)时,复制才有效。读取文件的事情是有问题的。导致死锁。
    • @MangeshGhotage 我已经更新了我的代码以使用AsTask
    猜你喜欢
    • 2019-06-04
    • 1970-01-01
    • 2019-04-05
    • 1970-01-01
    • 2016-12-02
    • 1970-01-01
    • 1970-01-01
    • 2013-02-13
    • 2014-08-14
    相关资源
    最近更新 更多