如何在具有返回类型的方法中调用异步方法



这是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;

最新更新