【问题标题】:How to return an object from an async method (c# - xamarin.forms)?如何从异步方法(c# - xamarin.forms)返回对象?
【发布时间】:2017-03-14 14:36:18
【问题描述】:

在 Xamarin.Forms 应用程序中,我使用 GeoLocator 检索有关我的位置的信息。 该方法是异步的。我必须返回包含位置参数的对象,但我不能。

这是我的类 PositionPage 的构造函数

public PositionPage()
{           
    getCurrentPosition();

    var map = new Map(
    MapSpan.FromCenterAndRadius(
        new Position(45.987487, 9.366337), Distance.FromMiles(0.3)))
        {
            IsShowingUser = true,
            HeightRequest = 100,
            WidthRequest = 960,
            VerticalOptions = LayoutOptions.FillAndExpand
        };
    var stack = new StackLayout { Spacing = 0 };
    stack.Children.Add(map);
    Content = stack;            
}

这是 async 方法(现在是 void 方法):

public async void getCurrentPosition() 
{
    try
    {
        var locator = CrossGeolocator.Current;
        locator.DesiredAccuracy = 50;

        var position = await locator.GetPositionAsync(timeoutMilliseconds: 10000);

        Debug.WriteLine("Position Status: {0}", position.Timestamp);
        Debug.WriteLine("Position Latitude: {0}", position.Latitude);
        Debug.WriteLine("Position Longitude: {0}", position.Longitude);                
    }
    catch (Exception ex)
    {
        Debug.WriteLine("Unable to get location, may need to increase timeout: " + ex);
    }
}

我需要在调用 getCurrentPosition() 时返回一个位置对象以将它传递到 MapSpan 中。

我该怎么做?

【问题讨论】:

  • 你不应该从构造函数调用异步方法,我强烈建议你重新考虑你的代码设计。

标签: c# asynchronous xamarin xamarin.forms async-await


【解决方案1】:

其他答案似乎都没有解决您的代码的主要问题,因此我将添加我的答案。

你有一个异步方法getCurrentPosition()。此方法等待调用:locator.GetPositionAsync(timeoutMilliseconds: 10000)。此代码的唯一问题是您没有返回从locator.GetPositionAsync() 获得的结果。你可以通过这样做来解决这个问题:

//SomeType needs to be the same type that locator.GetPositionAsync() returns
//Also C# naming conventions say that any async methods should end with "Async", 
//obviously not required but it will make your life easier later
public async Task<SomeType> GetCurrentPositionAsync() 
{
    SomeType position = null;
    try
    {
        var locator = CrossGeolocator.Current;
        locator.DesiredAccuracy = 50;

        position = await locator.GetPositionAsync(timeoutMilliseconds: 10000);
        Debug.WriteLine("Position Status: {0}", position.Timestamp);
        Debug.WriteLine("Position Latitude: {0}", position.Latitude);
        Debug.WriteLine("Position Longitude: {0}", position.Longitude);            
    }
    catch (Exception ex)
    {
        Debug.WriteLine("Unable to get location, may need to increase timeout: " + ex);
    }
    return position;
}

您遇到的最大问题是您的构造函数试图调用异步方法,这将是一个同步调用,它基本上否定了异步/等待的好处。

我会强烈建议更改此应用程序的设计,以便您利用 async/await。这不一定是重大变化。您可以实现一个静态异步方法来构造一个对象并为您返回它,这样您就可以等待该调用。像这样的:

public class PositionPage
{
    public static async Task<PositionPage> CreateAsync()
    {
        var position = await GetCurrentPositionAsync();

        var map = new Map(
            MapSpan.FromCenterAndRadius(
                new Position(45.452481, 9.166337),
                Distance.FromMiles(0.3)))
        {
            IsShowingUser = true,
            HeightRequest = 100,
            WidthRequest = 960,
            VerticalOptions = LayoutOptions.FillAndExpand
        };
        var stack = new StackLayout { Spacing = 0 };
        stack.Children.Add(map);


        var positionPage= new PositionPage();
        positionPage.Content = stack;
        return positionPage;
    }

    private PositionPage()
    {

    }

    //SomeType needs to be the same type that locator.GetPositionAsync() returns
    //Also C# naming conventions say that any async methods should end with "Async", obviously not required but it will make your life easier later
    public async Task<SomeType> GetCurrentPositionAsync() 
    {
        try
        {
            var locator = CrossGeolocator.Current;
            locator.DesiredAccuracy = 50;

            var position = await locator.GetPositionAsync(timeoutMilliseconds: 10000);
            Debug.WriteLine("Position Status: {0}", position.Timestamp);
            Debug.WriteLine("Position Latitude: {0}", position.Latitude);
            Debug.WriteLine("Position Longitude: {0}", position.Longitude);
        }
        catch (Exception ex)
        {
            Debug.WriteLine("Unable to get location, may need to increase timeout: " + ex);
        }
    }
}

【讨论】:

    【解决方案2】:

    你需要从你的async方法返回Task&lt;T&gt;,不要让它返回void,你需要像这样重构方法:

    public async Task<Geoposition> getCurrentPosition() 
    {
      Geoposition position = null;
      try
      {
         var locator = CrossGeolocator.Current;
         locator.DesiredAccuracy = 50;
    
         position = await locator.GetPositionAsync(timeoutMilliseconds: 10000);
    
         Debug.WriteLine("Position Status: {0}", position.Timestamp);
         Debug.WriteLine("Position Latitude: {0}", position.Latitude);
         Debug.WriteLine("Position Longitude: {0}", position.Longitude);
    
      }
      catch (Exception ex)
      {
         Debug.WriteLine("Unable to get location, may need to increase timeout: " + ex);
      }
    
      return position;
    
    }
    

    在调用方,您还需要将该方法设置为async,以实现理想的场景,但如果您想同步调用异步方法,那么在调用方将是这样的:

    var position = getCurrentPosition().Result;
    // please note that it will be blocking call
    // it is synchronously calling the async method
    

    我不知道GetLocationAsync 的返回类型是什么,但它会返回Task&lt;T&gt;,其中T 是某种特定类型,所以最好将方法的返回类型设置为Task&lt;Geoposition&gt;,假设它返回Task&lt;Geoposition&gt;,但您可以将其替换为GetPositionAsync 方法的返回类型。

    或者,您可能希望保持 getCurrentPosition 同步,可以这样做:

    public Geoposition getCurrentPosition() 
    {
      Geoposition position = null;
      try
      {
         var locator = CrossGeolocator.Current;
         locator.DesiredAccuracy = 50;
    
         position = locator.GetPositionAsync(timeoutMilliseconds: 10000).Result;
    
         Debug.WriteLine("Position Status: {0}", position.Timestamp);
         Debug.WriteLine("Position Latitude: {0}", position.Latitude);
         Debug.WriteLine("Position Longitude: {0}", position.Longitude);
    
      }
      catch (Exception ex)
      {
         Debug.WriteLine("Unable to get location, may need to increase timeout: " + ex);
      }
    
      return position;
    
    }
    

    希望对你有帮助!

    【讨论】:

    • OP 正在从控制器同步调用此方法,返回 Task 而不是 void 不会解决他们的问题
    【解决方案3】:

    GetPositionAsync返回Task&lt;Position&gt;

    我认为你应该使用

    public async Task<Position> getCurrentPosition() {
    
            Position position = null;
    
            try
            {
                var locator = CrossGeolocator.Current;
                locator.DesiredAccuracy = 50;
    
                position = await locator.GetPositionAsync(timeoutMilliseconds: 10000);
    
                Debug.WriteLine("Position Status: {0}", position.Timestamp);
                Debug.WriteLine("Position Latitude: {0}", position.Latitude);
                Debug.WriteLine("Position Longitude: {0}", position.Longitude);
    
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Unable to get location, may need to increase timeout: " + ex);
            }
    
            return position;
        }
    

    【讨论】:

      【解决方案4】:

      GetCurrentPosition 现在有一个返回类型 void,所以如果你想返回一个特定类型的值,你应该定义返回类型。因为它也是一个异步函数,所以 GetCurrentPosition 的返回类型应该是这样的 Task:

      public async Task<Position> getCurrentPosition() { 
        var position = await locator.GetPositionAsync(timeoutMilliseconds: 10000);
        return position;
      }
      

      另外,调用者函数应该使用这样的等待:

      Public PositionPage() {
          Position position = await getCurrentPosition();
      }
      

      【讨论】:

      • 我试过了,但它只返回任务信息,如 asyncState、id、状态、异常和许多其他信息。与对象无关
      • 你得到这个的原因是因为你从你的构造函数中调用了方法。您永远不会获得任务的内容,因为您没有异步运行它。请参阅我对您帖子的评论。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-07-10
      • 2019-09-05
      • 2013-05-31
      • 2016-01-21
      • 1970-01-01
      • 2012-04-28
      相关资源
      最近更新 更多