关于c#:非静态方法所需的UWP对象引用

UWP object reference required from non-static method

我正在创建一个uwp应用程序,简单地说,我有一个async void getweather函数,它从API读取JSON并创建一个对象。调用forecast.getweather()函数,我得到错误"非静态方法需要对象引用"。我已经做了研究,但还没有找到一个解决这个问题的方法,因为我还不想返回一个对象。另外,如果这不可能(也许是个更好的主意),我将如何返回对象,以便它可以在整个应用程序的许多不同页面上使用,或者如果在void方法中创建,对象值是否仍然可以访问?

预报室

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    class Forecast
{
    public async void GetWeather()
    {
        var uri = new Uri("MY API URI HERE");
        using (HttpClient client = new HttpClient())
        {
            using (HttpResponseMessage response = await client.GetAsync(uri))
            {
                using (IHttpContent content = response.Content)
                {
                     var json = await content.ReadAsStringAsync();

                    var result = JsonConvert.DeserializeObject<RootObject>(json);
                    Debug.WriteLine("In async method");
                }
            }
        }
    }
}

主页

1
2
3
4
5
6
7
8
9
10
 public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();

        Forecast.GetWeather();

    }
}

天气预报

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
namespace WeatherForecast
{

public class Main
{
    public double temp { get; set; }
    public double temp_min { get; set; }
    public double temp_max { get; set; }
    public double pressure { get; set; }
    public double sea_level { get; set; }
    public double grnd_level { get; set; }
    public int humidity { get; set; }
    public double temp_kf { get; set; }
}

public class Weather
{
    public int id { get; set; }
    public string main { get; set; }
    public string description { get; set; }
    public string icon { get; set; }
}

public class Clouds
{
    public int all { get; set; }
}

public class Wind
{
    public double speed { get; set; }
    public double deg { get; set; }
}

public class Snow
{
    public double __invalid_name__3h { get; set; }
}

public class Sys
{
    public string pod { get; set; }
}

public class List
{
    public int dt { get; set; }
    public Main main { get; set; }
    public List<Weather> weather { get; set; }
    public Clouds clouds { get; set; }
    public Wind wind { get; set; }
    public Snow snow { get; set; }
    public Sys sys { get; set; }
    public string dt_txt { get; set; }
}

public class Coord
{
    public double lat { get; set; }
    public double lon { get; set; }
}

public class City
{
    public int id { get; set; }
    public string name { get; set; }
    public Coord coord { get; set; }
    public string country { get; set; }
}

public class RootObject
{
    public string cod { get; set; }
    public double message { get; set; }
    public int cnt { get; set; }
    public List<List> list { get; set; }
    public City city { get; set; }
}
}


问题是您的方法不是静态的,所以您需要创建一个Forecast类的实例来访问它。你可以在这个问题和这里阅读更多关于这个的内容。

快速解决方案还将使您的Forecast类静态(只要您不想有多个不同的实例):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static class Forecast
{
    public static async void GetWeather()
    {
        var uri = new Uri("MY API URI HERE");
        using (HttpClient client = new HttpClient())
        {
            using (HttpResponseMessage response = await client.GetAsync(uri))
            {
                using (IHttpContent content = response.Content)
                {
                     var json = await content.ReadAsStringAsync();

                    var result = JsonConvert.DeserializeObject<RootObject>(json);
                    Debug.WriteLine("In async method");
                }
            }
        }
    }
}

但现在我们遇到了一个更大的问题。你的方法是asyncasync void。这是您只有在绝对必要时才应该做的事情,因为async void方法是所谓的"火与忘"。它们开始,但是当它们到达第一个真正的异步调用时,它们将自己执行,并且如果在方法内部发生了不好的事情(如异常),在症状以一种难以调试的方式出现在其他地方之前,您永远不会知道它。另外,你永远不知道result什么时候可以使用。

最好的解决方案是Task返回类型。这就保证了在方法执行完成时结果是可用的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static class Forecast
{
    public static RootObject Result {get; private set;}

    public static async Task GetWeatherAsync()
    {
        var uri = new Uri("MY API URI HERE");
        using (HttpClient client = new HttpClient())
        {
            using (HttpResponseMessage response = await client.GetAsync(uri))
            {
                using (IHttpContent content = response.Content)
                {
                    var json = await content.ReadAsStringAsync();

                    Result = JsonConvert.DeserializeObject<RootObject>(json);

                    Debug.WriteLine("In async method");
                }
            }
        }
    }
}

您可以看到方法不再是void,但它仍然不直接返回RootObject(根据您的请求,否则您可以使用Task返回它)。我还添加了一个result属性,以便在执行完成时可以访问结果。因为类是static,所以在GetWeatherAsync方法调用完成后,可以从任何地方访问该属性。

你怎么用这个?您可以调用OnNavigatedTo处理程序中的方法,而不是构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public override async void OnNavigatedTo(NavigationEventArgs e)
{
    base.OnNavigatedTo( e );
    try
    {
       await Forecast.GetWeatherAsync();
       //after await finishes, Result is ready

       //do something with Forecast.Result
    }
    catch
    {
       //handle any exceptions
    }
}

你可能会注意到我违反了这样的规则:async方法不应该是void方法。但是,在本例中,我们将处理具有固定表示法的事件处理程序,因此您没有其他选择。但是,我添加了一个try-catch块,这样我们就可以在发生异常时处理它们。

我建议在文档中阅读更多关于async-await的内容,因为它将帮助您更好地理解这个概念。我还建议您查看关于实例与静态的更详细的信息,以便更好地了解它们之间的区别以及何时使用它们。