首页 文章

在构造函数中调用异步方法?

提问于
浏览
122

Summary :我想在构造函数中调用异步方法 . 这可能吗?

Details :我有一个名为 getwritings() 的方法来解析JSON数据 . 如果我只是在一个 async 方法中调用 getwritings() 并将 await 放在它的左边,那么一切正常 . 但是,当我在我的页面中创建 LongListView 并尝试填充它时,我发现 getWritings() 令人惊讶地返回 null 并且 LongListView 为空 .

为了解决这个问题,我尝试将 getWritings() 的返回类型更改为 Task<List<Writing>> ,然后通过 getWritings().Result 在构造函数中检索结果 . 然而,这样做最终 blocking the UI thread.

public partial class Page2 : PhoneApplicationPage
{
    List<Writing> writings;

    public Page2()
    {
        InitializeComponent();
        getWritings();
    }

    private async void getWritings()
    {
        string jsonData = await JsonDataManager.GetJsonAsync("1");
        JObject obj = JObject.Parse(jsonData);
        JArray array = (JArray)obj["posts"];

        for (int i = 0; i < array.Count; i++)
        {
            Writing writing = new Writing();
            writing.content = JsonDataManager.JsonParse(array, i, "content");
            writing.date = JsonDataManager.JsonParse(array, i, "date");
            writing.image = JsonDataManager.JsonParse(array, i, "url");
            writing.summary = JsonDataManager.JsonParse(array, i, "excerpt");
            writing.title = JsonDataManager.JsonParse(array, i, "title");

            writings.Add(writing);
        }

        myLongList.ItemsSource = writings;
    }
}

6 回答

  • 1

    你可以试试AsyncMVVM .

    Page2.xaml:

    <PhoneApplicationPage x:Class="Page2"
                          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
        <ListView ItemsSource="{Binding Writings}" />
    </PhoneApplicationPage>
    

    Page2.xaml.cs:

    public partial class Page2
    {
        InitializeComponent();
        DataContext = new ViewModel2();
    }
    

    ViewModel2.cs:

    public class ViewModel2: AsyncBindableBase
    {
        public IEnumerable<Writing> Writings
        {
            get { return Property.Get(GetWritingsAsync); }
        }
    
        private async Task<IEnumerable<Writing>> GetWritingsAsync()
        {
            string jsonData = await JsonDataManager.GetJsonAsync("1");
            JObject obj = JObject.Parse(jsonData);
            JArray array = (JArray)obj["posts"];
    
            for (int i = 0; i < array.Count; i++)
            {
                Writing writing = new Writing();
                writing.content = JsonDataManager.JsonParse(array, i, "content");
                writing.date = JsonDataManager.JsonParse(array, i, "date");
                writing.image = JsonDataManager.JsonParse(array, i, "url");
                writing.summary = JsonDataManager.JsonParse(array, i, "excerpt");
                writing.title = JsonDataManager.JsonParse(array, i, "title");
                yield return writing;
            }
        }
    }
    
  • 4

    尝试替换这个:

    myLongList.ItemsSource = writings;
    

    有了这个

    Dispatcher.BeginInvoke(() => myLongList.ItemsSource = writings);
    
  • 86

    简单地说,指的是Stephen Cleary https://stackoverflow.com/a/23051370/267000

    您的创建页面应该在构造函数中创建任务,您应该将这些任务声明为类成员或将其放在任务池中 .

    在这些任务期间获取您的数据,但是代码中应该等待这些任务,即在一些UI操作上,即Ok Click等 .

    我在WP中开发了这样的应用程序,我们在启动时创建了一大堆任务 .

  • 1

    最好的解决方案是确认下载和设计的异步性 .

    换句话说,在下载数据时确定应用程序的外观 . 让页面构造器设置该视图,然后开始下载 . 下载完成后,更新页面以显示数据 .

    我在asynchronous constructors上有一篇博文,你会发现它很有用 . 还有一些MSDN文章;一个在asynchronous data-binding(如果你正在使用MVVM)而另一个在asynchronous best practices上(即你应该避免 async void ) .

  • 38

    我想分享一种我一直用来解决这些问题的模式 . 我觉得它运作得相当好 . 当然,它只有在你可以控制调用构造函数的情况下才有效 . 以下示例

    public class MyClass
    {
        public static async Task<MyClass> Create()
        {
            var myClass = new MyClass();
            await myClass.Initialize();
            return myClass;
        }
    
        private MyClass()
        {
    
        }
    
        private async Task Initialize()
        {
            await Task.Delay(1000); // Do whatever asynchronous work you need to do
        }
    }
    

    基本上我们所做的是将构造函数设为私有,并创建我们自己的公共静态异步方法,该方法负责创建MyClass的实例 . 通过使构造函数私有并将静态方法保持在同一个类中,我们确保没有人可以“无意中”创建此类的实例而无需调用正确的初始化方法 . 围绕对象创建的所有逻辑仍包含在类中(仅在静态方法中) .

    var myClass1 = new MyClass() // Cannot be done, the constructor is private
    var myClass2 = MyClass.Create() // Returns a Task that promises an instance of MyClass once it's finished
    var myClass3 = await MyClass.Create() // asynchronously creates and initializes an instance of MyClass
    

    在当前场景中实现它看起来像:

    public partial class Page2 : PhoneApplicationPage
    {
        public static async Task<Page2> Create()
        {
            var page = new Page2();
            await page.getWritings();
            return page;
        }
    
        List<Writing> writings;
    
        private Page2()
        {
            InitializeComponent();
        }
    
        private async Task getWritings()
        {
            string jsonData = await JsonDataManager.GetJsonAsync("1");
            JObject obj = JObject.Parse(jsonData);
            JArray array = (JArray)obj["posts"];
    
            for (int i = 0; i < array.Count; i++)
            {
                Writing writing = new Writing();
                writing.content = JsonDataManager.JsonParse(array, i, "content");
                writing.date = JsonDataManager.JsonParse(array, i, "date");
                writing.image = JsonDataManager.JsonParse(array, i, "url");
                writing.summary = JsonDataManager.JsonParse(array, i, "excerpt");
                writing.title = JsonDataManager.JsonParse(array, i, "title");
    
                writings.Add(writing);
            }
    
            myLongList.ItemsSource = writings;
        }
    }
    

    而不是做

    var page = new Page2();
    

    你会做的

    var page = await Page2.Create();
    
  • 57

    你也可以这样做:

    Task.Run(() => this.FunctionAsync()).Wait();
    

相关问题