【问题标题】:How to call async method from not async method?如何从非异步方法调用异步方法?
【发布时间】:2017-03-06 00:30:50
【问题描述】:

我正在使用 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)
            {

            }
        } 

第三个功能是“更改日历”

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,第二个函数是 ChangeCalendar。完成第一个函数后,应该调用第二个函数。但在我的情况下,在 CallWebService() 函数中控制不是转到“If(response.flag==true)”这一行(控制超出函数)并直接调用 ChangeCalendar() 方法。

标签: c# xamarin xamarin.ios xamarin.android xamarin.forms


【解决方案1】:

该问题未提供测试解决方案所需的完整源代码。

回答您关于在非异步方法中调用等待函数的问题。你可以使用

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) =>
{

});

【讨论】:

    【解决方案2】:

    尝试关注

    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)
                {
    
                }
            }
    

    你的网络服务应该是这样的

    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)
            {
    
            }
        }
    

    【讨论】:

    • 谢谢,我对如何从普通方法异步方法有点困惑。如何等待异步方法。你真是帮了我。
    • 很高兴为您提供帮助!!
    • 最后一个方法不需要返回任务或等待。除非您使用 async 修饰符,否则此代码不会编译。
    • 是的,你是对的@valdetero 我忘了把异步放在那里。
    【解决方案3】:

    我假设从 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 方法,这样应该更容易找到问题的原因

    【讨论】:

    • 谢谢,我对如何从普通方法异步方法有点困惑。如何等待异步方法。你真的帮了我。
    • 这应该是答案,因为它提供了一种更好的方法并且可以实际编译,而不是仅仅让所有东西都返回一个任务。
    • 您可能是对的,但这并没有提供有关如何使异步方法同步的答案。
    【解决方案4】:

    我从您的 cmets 中看到至少 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
      

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-07-20
      • 2020-03-10
      • 1970-01-01
      • 2021-04-18
      • 1970-01-01
      相关资源
      最近更新 更多