Вызов асинхронного метода в конструкторе?
резюме: я хотел бы вызвать асинхронный метод в конструкторе. Это возможно?
подробности: у меня есть метод, называемый getwritings() который анализирует данные JSON. Все работает нормально, если я просто позвоню getwritings() на async способ и поставить await слева от него. Однако , когда я создаю LongListView в моей странице и попытаться заполнить его я нахожу, что getWritings() удивительно, возвращающихся null и LongListView is пустой.
чтобы решить эту проблему, я попытался изменить тип возврата getWritings() до Task<List<Writing>> и затем получение результата в конструкторе через getWritings().Result. Однако, делая это заканчивается блокировки потока пользовательского интерфейса.
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 ответов:
лучшим решением является признание асинхронного характера загрузки и дизайна для него.
другими словами, решите, как должно выглядеть ваше приложение во время загрузки данных. Есть конструктор страниц это просмотр и запуск загрузки. Когда загрузка завершится обновление страница для отображения данных.
У меня есть сообщение в блоге на асинхронный конструкторы что вы можете найти полезным. Также, некоторые статьи MSDN; один на асинхронная привязка данных (если вы используете MVVM) и еще один на асинхронные рекомендации (т. е., вы должны избегать
async void).
Я хотел бы поделиться шаблоном, который я использую для решения таких проблем. Это работает довольно хорошо, я думаю. Конечно, это работает только если у вас есть контроль над тем, что вызывает конструктор. Пример ниже
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();
попробуйте заменить этот:
myLongList.ItemsSource = writings;С
Dispatcher.BeginInvoke(() => myLongList.ItemsSource = writings);
вы могли бы попробовать AsyncMVVM.
Страница2.xaml:
<PhoneApplicationPage x:Class="Page2" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <ListView ItemsSource="{Binding Writings}" /> </PhoneApplicationPage>Страница2.код 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; } } }
проще говоря, ссылаясь на Стивена Клири https://stackoverflow.com/a/23051370/267000
ваша страница при создании должна создавать задачи в конструкторе, и вы должны объявить эти задачи как члены класса или поместить их в пул задач.
ваши данные извлекаются во время этих задач, но эти задачи должны ожидаться в коде, т. е. на некоторых манипуляциях с пользовательским интерфейсом, т. е. Ok Click и т. д.
Я разработал такие приложения в WP, у нас была целая куча задачи, созданные при запуске.
Comments