это приведет к утечке памяти в C #

1

Я изучаю С# снова после многих лет. Я программировал С# еще в 2.0 дня. Язык действительно развился, и его фантастика. При этом я делаю универсальное приложение W8/WP8. В основном, когда приложение запускается, конструктор запускает метод. Этот метод проверяет соединение, и если соединение включено, программа протекает вперед.

private async void UpdateInformationSection(IUICommand command) {
    InformationModel GeneralInformationModel = new InformationModel
    {
        apistatus = await voip_service.isAPIEnabled(),
        apimessage = await voip_service.GetAPIMessage(),
        currentbalance = await voip_service.getBalance(),
        currentip = await voip_service.getIP()
    };

    if (GeneralInformationModel.apistatus == false) {
        var msgdialog = new MessageDialog(
            "Please go to voip.ms to enable your API. You will need to know the IP address of the device on which this application is installed",
            "API connection could not be established");
        // Add commands and set their callbacks; both buttons use the same callback function instead of inline event handlers

        msgdialog.Commands.Add(new UICommand(
            "Try again", new UICommandInvokedHandler(this.UpdateInformationSection)));

        // Show the message dialog
        await msgdialog.ShowAsync();
    }

    // set the data context for the first section of the hub
    // so we can use bindings.
    mainpagehub.Sections[0].DataContext = GeneralInformationModel;

Поэтому, если вы заметили, что если соединение завершилось неудачей, появится диалоговое окно с сообщением. Появляется кнопка "попробуйте еще раз". Когда пользователи нажимают эту кнопку, у нее есть функция "обратного вызова", связанная с ней (новые вещи для меня, я думаю, это как обработчик событий?). В любом случае, вместо того, чтобы кодировать новый метод, я сделал метод обратного вызова таким же, как и текущий метод, в котором был создан почтовый ящик. Так что в основном я добавил аргумент, поэтому у меня есть эта UpdateInformationSection(IUICommand command). И тогда функция обратного вызова является одним и тем же методом.

Чего я боюсь: каждый раз, когда они нажимают кнопку "повторить попытку", он уничтожит старый экземпляр? Другими словами, когда они нажимают кнопку "повторить попытку", завершается ли выполнение метода? В противном случае я представляю сценарий, когда метод вызывается снова и снова, и каждый метод застревает в неопределенности (если это имеет смысл).

Кроме того, в моем конструкторе при вызове метода FIRST мне пришлось изменить его на

//Update HUB Sections.            
// send null as argument since its not coming from a "command button"
// the argument is required when the API connection cant be established
// and thus a modal dialog comes up with a "try again" command button.
UpdateInformationSection(null);    

Можно ли отправить "нуль" в аргумент "command"? Какая здесь правильная процедура.

  • 0
    Вы никогда не используете переменную command в своем методе. Вы можете передать его как угодно, и ваш код не сможет определить разницу.
  • 0
    Да. Но функция обратного вызова нуждается в такой сигнатуре функции.
Показать ещё 4 комментария
Теги:

1 ответ

0
Лучший ответ

Конечно, здесь нет настоящей рекурсии, потому что вы используете async. Но возможно (возможно, фактически, но я не дважды проверял), что MessageDialog не завершает ShowAsync() метода ShowAsync() до тех пор, пока ваш собственный делегат команды не завершится. Это приведет к тому, что множественные экземпляры MessageDialog доступными до тех пор, пока вы, наконец, не покажете его, не позволяя им собирать мусор (т.е. Ближе всего к реальной утечке памяти с управляемыми объектами).

IMHO, этот метод будет лучше реализован, если вы избежите этого потенциального повторного входа, путем очередности повторного запуска метода вместо его прямого вызова. Это может выглядеть примерно так:

private async void UpdateInformationSection(IUICommand command) {
    InformationModel GeneralInformationModel = new InformationModel
{
    apistatus = await voip_service.isAPIEnabled(),
    apimessage = await voip_service.GetAPIMessage(),
    currentbalance = await voip_service.getBalance(),
    currentip = await voip_service.getIP()
};

if (GeneralInformationModel.apistatus == false) {
    var msgdialog = new MessageDialog(
        "Please go to voip.ms to enable your API. You will need to know the IP address of the device on which this application is installed",
        "API connection could not be established");
    // Add commands and set their callbacks; both buttons use the same callback function instead of inline event handlers

    msgdialog.Commands.Add(new UICommand("Try again"));

    // Show the message dialog
    await msgdialog.ShowAsync();

    var _ = CoreWindow.GetForCurrentThread().Dispatcher
        .RunAsync(CoreDispatcherPriority.Normal,
            () => { var ignoreTask = UpdateInformationSection(command); });
    return;
}

// set the data context for the first section of the hub
// so we can use bindings.
mainpagehub.Sections[0].DataContext = GeneralInformationModel;

Таким образом, каждый раз, когда отображается MessageDialog, он дает возможность идти вперед и закрываться, прежде чем перерисовывать его.

Вышеприведенное предполагает, что "Попробуйте еще раз" - это единственный вариант, который вы представляете. Конечно, если у вас есть дополнительные параметры, вы можете использовать объект UICommand, чтобы различать выбранную опцию и делать соответствующую вещь; "Попробовать еще раз" повторит вышеупомянутый вызов RunAsync(), в то время как другие параметры будут делать то, что они делают.

Все, что сказало, лично я думаю, что было бы лучше избежать этой картины. Предположительно, пользователь сделал что-то еще, что первоначально инициировало это диалоговое окно. По крайней мере, также должен быть вариант "Отмена" в качестве альтернативы "Попробовать снова". И ИМХО было бы лучше просто представить это как предупреждение с по умолчанию "Закрыть", чтобы пользователь просто возвращался туда, где бы они ни находились, чтобы после исправления проблемы конфигурации они могли просто явно попытаться выполнить действие/снова.

Я, конечно, делаю некоторые предположения о программе здесь. Не имея конкретных подробностей, я признаю, что может быть какая-то веская причина сделать это так, как вы сейчас. Но, по крайней мере, убедитесь, что это действительно лучший способ сделать это. Придерживание пользователя в потенциально бесконечном цикле кажется немного "выключенным" для меня. :)

РЕДАКТИРОВАТЬ:

Чтобы уточнить этот бит кода:

    var _ = CoreWindow.GetForCurrentThread().Dispatcher
        .RunAsync(CoreDispatcherPriority.Normal,
            () => { var ignoreTask = UpdateInformationSection(command); });

Метод RunAsync() заставляет данный делегат выполняться в потоке Dispatcher, то есть поток пользовательского интерфейса для вашей программы. Здесь метод (предположительно) уже запущен, так как это команда, вызываемая некоторым объектом пользовательского интерфейса. Выполнение этого способа позволяет повторить вызов метода, но не повторным способом. То есть текущий вызов метода разрешен для завершения и возврата до следующего запуска. Это устраняет любой рекурсивный аспект.

() => { var ignoreTask = UpdateInformationSection(command); } сам делегат - () => { var ignoreTask = UpdateInformationSection(command); } () => { var ignoreTask = UpdateInformationSection(command); } - использование синтаксиса lambda body оператора оператора - это просто вызов метода для повторного вызова вашего метода команды.

Наконец, как метод RunAsync() и ваш метод команды являются async методами, возвращая экземпляр Task. В этом конкретном случае нам не нужно ждать, пока они закончатся, так что не await, но если мы ничего не сделаем с возвращаемым значением, компилятор будет генерировать предупреждение. Для меня самый простой и самый чистый способ подавить предупреждение - это продолжить и скопировать ссылку " Task на локальную переменную, которой достаточно, чтобы сделать компилятор счастливым. Метод RunAsync() копируется в переменную с именем _, что я обычно использую для переменных, которые мне действительно не нужны, в то время как значение возвращаемого метода команды копируется в переменную с именем ignoreTask, названной так, чтобы быть явно о цели переменной (которая должна игнорировать Task возвращенную из вашего метода команды).

  • 0
    Спасибо. Отвечает на мой вопрос perfeclty. Можете ли вы описать свой код var _ = CoreWindow.GetForCurrentThread().Dispatcher .RunAsync(CoreDispatcherPriority.Normal, () => { var ignoreTask = UpdateInformationSection(command); }); хотя? Я не понимаю, как это написано (может быть, у него есть новые функции из языка C # 3.0 +)
  • 0
    @masfenix: я добавил некоторые пояснения к этому коду. Я надеюсь, что это прояснит для вас.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню