簡體   English   中英

如何從非異步方法調用異步方法?

[英]How to call async method from not async method?

我正在以xamarin形式工作。 我試圖將Web服務綁定到日歷控件中。 請參閱以下日歷控件鏈接(XamForms.Controls.Calendar)。

https://github.com/rebeccaXam/XamForms.Controls.Calendar

第一個功能是創建7 * 6 = 42個標簽和按鈕,然后創建“ callWebService”方法來調用服務功能,這是一種異步方法,可從服務中獲取響應。

protected void FillCalendarWindows()
        {
            try
            {
                for (int r = 0; r < 6; r++)
                {
                    for (int c = 0; c < 7; c++)
                    {
                        if (r == 0)
                        {
                            labels.Add(new Label
                            {
                                HorizontalOptions = LayoutOptions.Center,
                                VerticalOptions = LayoutOptions.Center,
                                TextColor = Color.Black,
                                FontSize = 18,
                                FontAttributes = FontAttributes.Bold
                            });
                            DayLabels.Children.Add(labels.Last(), c, r);
                        }
                        buttons.Add(new CalendarButton
                        {
                            BorderRadius = 0,
                            BorderWidth = BorderWidth,
                            BorderColor = BorderColor,
                            FontSize = DatesFontSize,
                            BackgroundColor = DatesBackgroundColor,
                            HorizontalOptions = LayoutOptions.FillAndExpand,
                            VerticalOptions = LayoutOptions.FillAndExpand
                        });
                        buttons.Last().Clicked += DateClickedEvent;
                        MainCalendar.Children.Add(buttons.Last(), c, r);
                    }
                }
                flag = 1;
                //Device.BeginInvokeOnMainThread(() => CallWebService(StartDate.Month, StartDate.Year));
                CallWebService(StartDate.Month, StartDate.Year);
                //CallServiceInNewTask(StartDate.Month, StartDate.Year);
                //Device.BeginInvokeOnMainThread(() => ChangeCalendar(CalandarChanges.All));

            }
            catch (Exception e)
            {

            }
        }

第二個功能是“ callWebService”功能,我在列表收集對象中收集響應,然后調用“ ChangeClaendar”功能,該功能用於綁定標簽和按鈕文本並填充適當的顏色。

public async void CallWebService(int Month, int Year)
        {
            try
            {
                var response = await GetResponseFromWebService.GetResponse<ServiceClasses.RootObject_AttendanceTable>(ServiceURL.GetAttendanceTableList + "Month=" + Month + "&Year=" + Year + "&EmpCd=" + _empCode);
                if (response.Flag == true)
                {
                    if (ListObjAttendanceTblList == null)
                    {
                        ListObjAttendanceTblList = new List<LstAttendanceDtl>();
                    }
                    for (int i = 0; i < response.lstAttendanceDtl.Count; i++)
                    {
                        var objAttendanceTableList = new LstAttendanceDtl();

                        objAttendanceTableList.AttendanceDt = response.lstAttendanceDtl[i].AttendanceDt;
                        objAttendanceTableList.EarlyDeparture = response.lstAttendanceDtl[i].EarlyDeparture;
                        objAttendanceTableList.InTime = response.lstAttendanceDtl[i].EarlyDeparture;
                        objAttendanceTableList.LateArrival = response.lstAttendanceDtl[i].EarlyDeparture;
                        objAttendanceTableList.OutTime = response.lstAttendanceDtl[i].OutTime;
                        objAttendanceTableList.OverTime = response.lstAttendanceDtl[i].OverTime;
                        objAttendanceTableList.Reason = response.lstAttendanceDtl[i].Reason;
                        objAttendanceTableList.Remark = response.lstAttendanceDtl[i].Remark;
                        objAttendanceTableList.Shift = response.lstAttendanceDtl[i].Shift;
                        objAttendanceTableList.WorkingHrs = response.lstAttendanceDtl[i].WorkingHrs;

                        ListObjAttendanceTblList.Add(objAttendanceTableList);
                    }
                }
                else
                {
                }
                if (flag == 1)
                {
                    ChangeCalendar(CalandarChanges.All);
                }
                else
                {
                    ChangeCalendar(CalandarChanges.StartDate);
                }
            }
            catch (WebException e)
            {

            }
        } 

第三個功能是“ ChangeCalendar”

protected void ChangeCalendar(CalandarChanges changes)
        {
            try
            {
                if (changes.HasFlag(CalandarChanges.StartDate))
                {
                    Device.BeginInvokeOnMainThread(() => CenterLabel.Text = StartDate.ToString(TitleLabelFormat));
                }
                var start = CalendarStartDate;
                var beginOfMonth = false;
                var endOfMonth = false;
                for (int i = 0; i < buttons.Count; i++)
                {
                    endOfMonth |= beginOfMonth && start.Day == 1;
                    beginOfMonth |= start.Day == 1;

                    LstAttendanceDtl objAttendanceDtl = ListObjAttendanceTblList.Find(s => s.AttendanceDt.Equals(start.Date.ToString("dd/MM/yyyy")));
                    string remarks = string.Empty;

                    if (i < 7 && WeekdaysShow && changes.HasFlag(CalandarChanges.StartDay))
                    {
                        Device.BeginInvokeOnMainThread(() => labels[i].Text = start.ToString(WeekdaysFormat));
                        //labels[i].Text = start.ToString(WeekdaysFormat);
                        //DateTime d = Convert.ToDateTime(objAttendanceDtl.AttendanceDt).Date; 
                    }
                    if (changes.HasFlag(CalandarChanges.All))
                    {
                        Device.BeginInvokeOnMainThread(()=>buttons[i].Text = string.Format("{0}", start.Day));
                        //buttons[i].Text = string.Format("{0}", start.Day);
                    }
                    else
                    {
                        Device.BeginInvokeOnMainThread(() => buttons[i].TextWithoutMeasure = string.Format("{0}", start.Day));
                    }

                    buttons[i].Date = start;
                    var isInsideMonth = beginOfMonth && !endOfMonth;
                    if (objAttendanceDtl != null)
                    {
                        remarks = objAttendanceDtl.Remark;

                        if ((remarks.ToLower()).Trim() == stringFullDay.ToLower().Trim())
                        {
                            SetButtonPresent(buttons[i], isInsideMonth);
                        }
                        else if (remarks.ToLower().Trim() == stringAbsent.ToLower().Trim())
                        {
                            SetButtonAbsent(buttons[i], isInsideMonth);
                        }
                        else if (remarks.ToLower().Trim() == stringWeekOff.ToLower().Trim())
                        {
                            SetButtonWeekendMood(buttons[i], isInsideMonth);
                        }
                        else if (remarks.ToLower().Trim() == stringHolidays.ToLower().Trim())
                        {
                            SetButtonHolidays(buttons[i], isInsideMonth);
                        }
                        else if (remarks.ToLower().Trim() == stringSecondhalfAbsent.ToLower().Trim() ||
                            remarks.ToLower().Trim() == stringFirsthalfAbsent.ToLower().Trim())
                        {
                            SetButtonHalfDayMood(buttons[i], isInsideMonth);
                        }
                        else
                        {
                            SetButtonDisabled(buttons[i]);
                        }
                    }
                    else
                    {
                        SetButtonOutSideMonth(buttons[i]);
                    }
                    SpecialDate sd = null;
                    if (SpecialDates != null)
                    {
                        sd = SpecialDates.FirstOrDefault(s => s.Date.Date == start.Date);
                    }

                    if (sd != null)
                    {
                        SetButtonSpecial(buttons[i], sd);
                    }
                    else if (SelectedDate.HasValue && start.Date == SelectedDate.Value.Date)
                    {
                        SetButtonSelected(buttons[i], isInsideMonth);
                    }
                    start = start.AddDays(1);
                }
            }
            catch (Exception e)
            {

            }
        }

問題是:

1.當我嘗試直接填寫標簽列表時,在“ Changecalendar”功能中

labels[i].Text = start.ToString(WeekdaysFormat);

它顯示了一個錯誤

“ UIKit一致性錯誤:您正在調用只能從UI線程調用的UIKit方法。” 所以為了消除這個錯誤,我寫了

Device.BeginInvokeOnMainThread(() => labels[i].Text = start.ToString(WeekdaysFormat));

Device.BeginInvokeOnMainThread(()=>buttons[i].Text = string.Format("{0}", start.Day));

Device.BeginInvokeOnMainThread(() => buttons[i].TextWithoutMeasure = string.Format("{0}", start.Day));

但它顯示我一個錯誤

System.ArgumentOutOfRangeException:索引超出范圍。 必須為非負數並且小於集合的大小。

2.如果將函數“ CallWebService”和“ ChangeCalendar”都一一放在“ FillCalendarWindow”中,則列表對象未綁定,控件從該函數中退出,直接調用ChangeCalendar函數,並給了我空引用對象。

該問題未提供測試解決方案所需的完整源代碼。

回答有關在非異步方法中調用等待函數的問題。 您可以使用

CallWebService().Wait(optional timeout); 

要么

CallWebService().GetAwaiter().GetResult(); 

您還應該更改函數定義

async void CallWebService(int Month, int Year)

async Task CallWebService(int Month, int Year);

正確處理異常和線程切換

如果可以在不阻塞的情況下調用CallWebService,那么您也可以

CallWebService(1,2).ContinueWith((task) =>
{

});

我假設從UI線程調用了FillCalendarWindows()方法,但是您不希望UI線程在外部操作GetResponseFromWebService.GetResponse發生時等待並凍結控件,這就是為什么要在內部進行異步調用的原因CallWebService()。

我認為使用異步CallWebService的更好方法是使用“一路異步”,因此您必須將CallWebService上游的所有方法都更改為異步方法。 這具有額外的好處,您無需執行Device.BeginInvokeOnMainThread,因為async / await捕獲了調用方的同步上下文,因此將在UI線程上調用ChangeCalendar。

async void SomeEventHandler()
{
// called from the UI thread (or its equivalent in Xamarin)
    await FillCalendarWindows();
}

protected async Task FillCalendarWindows()
    {
        try
        {
           //create 7*6 = 42 labels and buttons

            await CallWebService(StartDate.Month, StartDate.Year);

        }
        catch (Exception e)
        {

        }
    }

public async Task CallWebService(int Month, int Year)
    {
        try
        {
            await GetResponseFromWebService.GetResponse... ;

            // .... same code as in your example 

            ChangeCalendar(....);

        }
        catch /*... */
        {

        }
    } 

protected void ChangeCalendar(int changes)
    {
        try
        {
            /* no need to do Device.BeginInvokeOnMainThread () so you can replace all that with normal calls*/
        }
        catch (Exception e)
        {
            /* ... */
        }
    }

不知道如何引發System.ArgumentOutOfRangeException,我無法在github上找到正確的代碼版本,因此無法調查該特定錯誤。 我的猜測是,您有多個線程正在修改“按鈕”集合,並且在調用Device.BeginInvokeOnMainThread時,您可能會發現該集合中的元素少於預期的數量。

TL; DR:始終使用async / await而不是以同步方式調用async方法,這應該使查找問題原因更加容易

嘗試跟隨

protected async Task FillCalendarWindows()
        {
            try
            {
                for (int r = 0; r < 6; r++)
                {
                    for (int c = 0; c < 7; c++)
                    {
                        if (r == 0)
                        {
                            labels.Add(new Label
                            {
                                HorizontalOptions = LayoutOptions.Center,
                                VerticalOptions = LayoutOptions.Center,
                                TextColor = Color.Black,
                                FontSize = 18,
                                FontAttributes = FontAttributes.Bold
                            });
                            DayLabels.Children.Add(labels.Last(), c, r);
                        }
                        buttons.Add(new CalendarButton
                        {
                            BorderRadius = 0,
                            BorderWidth = BorderWidth,
                            BorderColor = BorderColor,
                            FontSize = DatesFontSize,
                            BackgroundColor = DatesBackgroundColor,
                            HorizontalOptions = LayoutOptions.FillAndExpand,
                            VerticalOptions = LayoutOptions.FillAndExpand
                        });
                        buttons.Last().Clicked += DateClickedEvent;
                        MainCalendar.Children.Add(buttons.Last(), c, r);
                    }
                }
                flag = 1;
                //Device.BeginInvokeOnMainThread(() => CallWebService(StartDate.Month, StartDate.Year));
                await CallWebService(StartDate.Month, StartDate.Year);
                //CallServiceInNewTask(StartDate.Month, StartDate.Year);
                //Device.BeginInvokeOnMainThread(() => await ChangeCalendar(CalandarChanges.All));

            }
            catch (Exception e)
            {

            }
        }

您的Web服務應該像

public async Task CallWebService(int Month, int Year)
    {
        try
        {
            var response = await GetResponseFromWebService.GetResponse<ServiceClasses.RootObject_AttendanceTable>(ServiceURL.GetAttendanceTableList + "Month=" + Month + "&Year=" + Year + "&EmpCd=" + _empCode);
            if (response.Flag == true)
            {
                if (ListObjAttendanceTblList == null)
                {
                    ListObjAttendanceTblList = new List<LstAttendanceDtl>();
                }
                for (int i = 0; i < response.lstAttendanceDtl.Count; i++)
                {
                    var objAttendanceTableList = new LstAttendanceDtl();

                    objAttendanceTableList.AttendanceDt = response.lstAttendanceDtl[i].AttendanceDt;
                    objAttendanceTableList.EarlyDeparture = response.lstAttendanceDtl[i].EarlyDeparture;
                    objAttendanceTableList.InTime = response.lstAttendanceDtl[i].EarlyDeparture;
                    objAttendanceTableList.LateArrival = response.lstAttendanceDtl[i].EarlyDeparture;
                    objAttendanceTableList.OutTime = response.lstAttendanceDtl[i].OutTime;
                    objAttendanceTableList.OverTime = response.lstAttendanceDtl[i].OverTime;
                    objAttendanceTableList.Reason = response.lstAttendanceDtl[i].Reason;
                    objAttendanceTableList.Remark = response.lstAttendanceDtl[i].Remark;
                    objAttendanceTableList.Shift = response.lstAttendanceDtl[i].Shift;
                    objAttendanceTableList.WorkingHrs = response.lstAttendanceDtl[i].WorkingHrs;

                    ListObjAttendanceTblList.Add(objAttendanceTableList);
                }
            }
            else
            {
            }
            if (flag == 1)
            {
               await ChangeCalendar(CalandarChanges.All);
            }
            else
            {
               await ChangeCalendar(CalandarChanges.StartDate);
            }
        }
        catch (WebException e)
        {

        }
    } 

而且您的ChnageCalender方法應該像

protected async Task ChangeCalendar(CalandarChanges changes)
    {
        try
        {
            if (changes.HasFlag(CalandarChanges.StartDate))
            {
                Device.BeginInvokeOnMainThread(() => CenterLabel.Text = StartDate.ToString(TitleLabelFormat));
            }
            var start = CalendarStartDate;
            var beginOfMonth = false;
            var endOfMonth = false;
            for (int i = 0; i < buttons.Count; i++)
            {
                endOfMonth |= beginOfMonth && start.Day == 1;
                beginOfMonth |= start.Day == 1;

                LstAttendanceDtl objAttendanceDtl = ListObjAttendanceTblList.Find(s => s.AttendanceDt.Equals(start.Date.ToString("dd/MM/yyyy")));
                string remarks = string.Empty;

                if (i < 7 && WeekdaysShow && changes.HasFlag(CalandarChanges.StartDay))
                {
                    Device.BeginInvokeOnMainThread(() => labels[i].Text = start.ToString(WeekdaysFormat));
                    //labels[i].Text = start.ToString(WeekdaysFormat);
                    //DateTime d = Convert.ToDateTime(objAttendanceDtl.AttendanceDt).Date; 
                }
                if (changes.HasFlag(CalandarChanges.All))
                {
                    Device.BeginInvokeOnMainThread(()=>buttons[i].Text = string.Format("{0}", start.Day));
                    //buttons[i].Text = string.Format("{0}", start.Day);
                }
                else
                {
                    Device.BeginInvokeOnMainThread(() => buttons[i].TextWithoutMeasure = string.Format("{0}", start.Day));
                }

                buttons[i].Date = start;
                var isInsideMonth = beginOfMonth && !endOfMonth;
                if (objAttendanceDtl != null)
                {
                    remarks = objAttendanceDtl.Remark;

                    if ((remarks.ToLower()).Trim() == stringFullDay.ToLower().Trim())
                    {
                        SetButtonPresent(buttons[i], isInsideMonth);
                    }
                    else if (remarks.ToLower().Trim() == stringAbsent.ToLower().Trim())
                    {
                        SetButtonAbsent(buttons[i], isInsideMonth);
                    }
                    else if (remarks.ToLower().Trim() == stringWeekOff.ToLower().Trim())
                    {
                        SetButtonWeekendMood(buttons[i], isInsideMonth);
                    }
                    else if (remarks.ToLower().Trim() == stringHolidays.ToLower().Trim())
                    {
                        SetButtonHolidays(buttons[i], isInsideMonth);
                    }
                    else if (remarks.ToLower().Trim() == stringSecondhalfAbsent.ToLower().Trim() ||
                        remarks.ToLower().Trim() == stringFirsthalfAbsent.ToLower().Trim())
                    {
                        SetButtonHalfDayMood(buttons[i], isInsideMonth);
                    }
                    else
                    {
                        SetButtonDisabled(buttons[i]);
                    }
                }
                else
                {
                    SetButtonOutSideMonth(buttons[i]);
                }
                SpecialDate sd = null;
                if (SpecialDates != null)
                {
                    sd = SpecialDates.FirstOrDefault(s => s.Date.Date == start.Date);
                }

                if (sd != null)
                {
                    SetButtonSpecial(buttons[i], sd);
                }
                else if (SelectedDate.HasValue && start.Date == SelectedDate.Value.Date)
                {
                    SetButtonSelected(buttons[i], isInsideMonth);
                }
                start = start.AddDays(1);
            }
        }
        catch (Exception e)
        {

        }
    }

我從您的評論中至少看到2個問題

  1. “ If(response.flag == true)(控制權從函數中消失了,直接調用ChangeCalendar()-顯然是因為您沒有等待。

  2. 按鈕的數量(42)與標簽的數量(7)不同,因此,當您嘗試使用相同的“ i”去標簽[i]和按鈕[i]時,會得到標簽索引的ArgumentOutOfRangeException。 注意if(r==0) ,它將標簽數限制為7。

      for (int r = 0; r < 6; r++) { for (int c = 0; c < 7; c++) { if (r == 0) { labels.Add(new Label 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM