使用LINQ重构并降低圈复杂度:优化默认短信手机号获取方法
重构获取默认短信手机号的LINQ实现
需求规则
- 返回的号码必须满足
sms_capable == 1 - 优先选择
last_used_for_rx == 1的号码 - 若没有上述号码,按 Other(类型ID 9) → Home(类型ID 1) → Office(类型ID 3) 的顺序返回第一个手机号非空的有效号码
原实现代码
string GetDefaultSMSPhoneNumber(IEnumerable<PhoneNumbers> patientNumbers) { const int PHONE_TYPE_HOME = 1; const int PHONE_TYPE_OFFICE = 3; const int PHONE_TYPE_OTHER = 9; var phoneNumberByType = patientNumbers.Where(p => p.sms_capable == 1).GroupBy(p => p.phone_type_id); // Select the phone number last used in creating a prescription if (patientNumbers.Where(p => p.sms_capable == 1 && p.last_used_for_rx == 1).Count() > 0) { return patientNumbers.Where(p => p.sms_capable == 1 && p.last_used_for_rx == 1).FirstOrDefault().phone_number; } // If no number has been used, select a configured SMS number in the following order (Other, Home, Office) if (patientNumbers.Where(p => p.sms_capable == 1 && p.phone_type_id == PHONE_TYPE_OTHER).Count() > 0) { return patientNumbers.Where(p => p.sms_capable == 1 && p.phone_type_id == PHONE_TYPE_OTHER).FirstOrDefault().phone_number; } // If no number has been used, select a configured SMS number in the following order (Other, Home, Office) if (patientNumbers.Where(p => p.sms_capable == 1 && p.phone_type_id == PHONE_TYPE_HOME).Count() > 0) { return patientNumbers.Where(p => p.sms_capable == 1 && p.phone_type_id == PHONE_TYPE_HOME).FirstOrDefault().phone_number; } // If no number has been used, select a configured SMS number in the following order (Other, Home, Office) if (patientNumbers.Where(p => p.sms_capable == 1 && p.phone_type_id == PHONE_TYPE_OFFICE).Count() > 0) { return patientNumbers.Where(p => p.sms_capable == 1 && p.phone_type_id == PHONE_TYPE_OFFICE).FirstOrDefault().phone_number; } return string.Empty; }
尝试的重构代码(存在问题)
string GetDefaultSMSPhoneNumber(IEnumerable<PhoneNumbers> patientNumbers) { const int PHONE_TYPE_HOME = 1; const int PHONE_TYPE_OFFICE = 3; const int PHONE_TYPE_OTHER = 9; var phoneNumberByType = patientNumbers.Where(p => p.sms_capable == 1).GroupBy(p => p.phone_type_id); var phoneNumber = patientNumbers.FirstOrDefault(p => p.sms_capable == 1 && p.last_used_for_rx == 1)?.phone_number; // Doesn't work if (string.IsNullOrEmpty(phoneNumber)) { var number = phoneNumberByType.FirstOrDefault(p => p.Key == PHONE_TYPE_OTHER && p.Where(x => !string.IsNullOrEmpty(x.phone_number)) || (p.Key == PHONE_TYPE_HOME && p.Where(x => !string.IsNullOrEmpty(x.phone_number)) || (p.Key == PHONE_TYPE_OFFICE && p.Where(x => !string.IsNullOrEmpty(x.phone_number))))); } }
优化后的实现方案
原代码存在多次遍历集合的性能问题,以下重构方案通过单次过滤+优先级排序实现高效简洁的逻辑,优先使用LINQ方法语法:
string GetDefaultSMSPhoneNumber(IEnumerable<PhoneNumbers> patientNumbers) { const int PHONE_TYPE_HOME = 1; const int PHONE_TYPE_OFFICE = 3; const int PHONE_TYPE_OTHER = 9; return patientNumbers // 先过滤出所有支持短信且手机号非空的有效号码,避免多次遍历集合 .Where(p => p.sms_capable == 1 && !string.IsNullOrEmpty(p.phone_number)) // 优先级排序:last_used_for_rx为1的号码排最前面 .OrderByDescending(p => p.last_used_for_rx == 1) // 按类型优先级排序:Other > Home > Office .ThenBy(p => p.phone_type_id switch { PHONE_TYPE_OTHER => 0, PHONE_TYPE_HOME => 1, PHONE_TYPE_OFFICE => 2, _ => int.MaxValue // 其他类型优先级最低 }) // 提取手机号 .Select(p => p.phone_number) // 取第一个符合条件的号码,无结果则返回空字符串 .FirstOrDefault() ?? string.Empty; }
方案说明
- 单次过滤:先筛选出所有满足
sms_capable == 1且手机号非空的号码,避免原代码中多次遍历整个集合的性能损耗 - 优先级排序:
- 用
OrderByDescending将last_used_for_rx == 1的号码排在最前面,保证优先选择 - 用
ThenBy结合switch表达式定义类型优先级,让Other类型先出现,其次是Home、Office
- 用
- 简洁链式调用:通过LINQ链式调用完成所有逻辑,代码更紧凑易读
替代方案(遍历优先级类型)
如果偏好更直观的顺序查找,也可以用以下写法:
string GetDefaultSMSPhoneNumber(IEnumerable<PhoneNumbers> patientNumbers) { const int PHONE_TYPE_HOME = 1; const int PHONE_TYPE_OFFICE = 3; const int PHONE_TYPE_OTHER = 9; // 一次性过滤有效号码,转为List避免多次枚举 var validSmsNumbers = patientNumbers .Where(p => p.sms_capable == 1 && !string.IsNullOrEmpty(p.phone_number)) .ToList(); // 优先取last_used的号码 var lastUsed = validSmsNumbers.FirstOrDefault(p => p.last_used_for_rx == 1); if (lastUsed != null) return lastUsed.phone_number; // 按优先级顺序查找类型匹配的号码 var priorityTypes = new[] { PHONE_TYPE_OTHER, PHONE_TYPE_HOME, PHONE_TYPE_OFFICE }; foreach (var type in priorityTypes) { var number = validSmsNumbers.FirstOrDefault(p => p.phone_type_id == type); if (number != null) return number.phone_number; } return string.Empty; }
原尝试代码的问题说明
- 条件中使用
p.Where(x => !string.IsNullOrEmpty(x.phone_number))错误:Where返回的是IEnumerable,不能直接作为布尔条件,应该用p.Any(x => !string.IsNullOrEmpty(x.phone_number))判断分组中是否有有效号码 - 实际上分组是多余的操作,直接按优先级顺序查找或排序取第一个更高效
内容的提问来源于stack exchange,提问作者jrandomuser
相关产品推荐
相关产品推荐

