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

时间:2014-11-24 07:38:14

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

这是windows phone 8.1 silverlight app。 我有一个文件关联。为此,我有一个班级

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

3 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

不幸的是,没有&#34;漂亮的方式&#34;覆盖非异步方法。

您可以做的最好的事情是确保在异步调用中添加ConfigureAwait(false)以确保SynchronizationContext没有流动和死锁,然后访问Result属性返回的Task

我要做的是更改读取文件的方法以返回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;