You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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;
}

方案说明

  1. 单次过滤:先筛选出所有满足sms_capable == 1且手机号非空的号码,避免原代码中多次遍历整个集合的性能损耗
  2. 优先级排序:
    • 用OrderByDescending将last_used_for_rx == 1的号码排在最前面,保证优先选择
    • 用ThenBy结合switch表达式定义类型优先级,让Other类型先出现,其次是Home、Office
  3. 简洁链式调用:通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 09:30:58