Xamarin.Forms 无法从服务类异步方法获取返回值到MainPage实现数据过滤
核心问题定位
- 调用
await myCourse.getCourses()时未接收方法返回值,异步执行完成后的返回对象被直接丢弃,这是拿不到数据的最直接原因 - 调用时机不合理:MainPage构造函数执行时就触发
CheckCourseComplete(),此时用户尚未输入任何内容,校验逻辑没有可匹配的依据 - 异步方法误用:
CheckCourseComplete使用async void签名,这类方法无法被其他代码安全等待,执行中抛出的异常会直接导致应用崩溃
符合单一职责原则的实现方案
整体逻辑严格拆分职责边界,不改动原有CourseService的核心定位:
CourseService仅承担「API请求、JSON反序列化」的纯数据获取职责,不加入任何业务校验、用户交互逻辑- 独立的校验方法单独承担「拉取全量课程数据、匹配用户输入的Id、触发页面跳转/错误提示」的逻辑,和服务层完全解耦
调整后的代码实现
优化后的CourseService(不改变原有职责)
public class CourseService : ICourseService { // 静态复用HttpClient,避免每次请求新建实例造成的Socket泄漏 private static readonly HttpClient client = new HttpClient(); string Base_Url = "https://www.test.com/api/TheCourse"; public async Task<ObservableCollection<Course>> getCourses() { try { HttpResponseMessage responseMessage = await client.GetAsync(Base_Url); if (responseMessage.StatusCode == System.Net.HttpStatusCode.OK) { var result = await responseMessage.Content.ReadAsStringAsync(); var deserializedCourses = JsonConvert.DeserializeObject<ObservableCollection<Course>>(result); return deserializedCourses; } return null; } catch (Exception) { throw; } } }
调整后的MainPage逻辑
namespace CourseMobile { public partial class MainPage : ContentPage { private string _getEntryText; // 页面内持有服务实例,避免每次校验都新建对象 private readonly ICourseService _courseService; public MainPage() { InitializeComponent(); _courseService = new CourseService(); BindingContext = new CourseViewModel(); // 移除构造函数里提前调用CheckCourseComplete的逻辑 } public string GetEntryText { get => _getEntryText; set => _getEntryText = value; } // 按钮点击事件保留async void签名,符合Xamarin事件处理规范 public async void OnOkGetCourseButton(object sender, EventArgs e) { var inputtedCourseNumber = GetEntryText?.Trim(); if (string.IsNullOrEmpty(inputtedCourseNumber)) { await DisplayAlert("", "Please enter your Course number", "OK"); return; } // 拿到有效用户输入后,再触发独立校验逻辑 await CheckCourseComplete(inputtedCourseNumber); } void Entry_TextChanged(object sender, TextChangedEventArgs e) { GetEntryText = e.NewTextValue; } // 校验方法改为async Task签名,支持异步等待,规避async void的崩溃风险 public async Task CheckCourseComplete(string userInputCourseId) { // 关键修复:接收getCourses的返回值,不丢弃数据 var allCourses = await _courseService.getCourses(); // 接口返回空值的兜底处理 if (allCourses == null || allCourses.Count == 0) { await DisplayAlert("Error", "Failed to load course data, please try later", "OK"); return; } // 类型转换兜底:根据Course.Id的实际类型适配用户输入 // 示例中假设Course.Id为int类型,可根据实际模型调整 if (!int.TryParse(userInputCourseId, out int inputId)) { await DisplayAlert("Error", "Please enter a valid numeric course number", "OK"); return; } // 匹配对应Id的课程条目 var matchedCourse = allCourses.FirstOrDefault(c => c.Id == inputId); if (matchedCourse != null) { // 匹配成功:将课程对象传递到详情页 await Navigation.PushAsync(new CourseDetailPage(matchedCourse)); } else { // 匹配失败:弹出提示 await DisplayAlert("Notice", "The course number you entered does not exist", "OK"); } } } }
方案说明
- 所有用户输入校验、数据匹配、页面交互的逻辑全部集中在
CheckCourseComplete独立方法中,没有侵入CourseService的实现,完全满足单一职责的设计要求 - 解决了之前调用异步方法丢弃返回值的问题,修正了不合理的调用时机,规避了async void导致的应用崩溃风险
- 增加了必要的空值、类型转换兜底,避免因为输入格式不对、接口返回异常导致的逻辑错误
内容的提问来源于stack exchange,提问作者Alice
相关产品推荐
相关产品推荐

