如何使用设计模式消除枚举属性赋值场景下的switch/case冗余代码
优化方案推荐
现有switch分支的写法最大的问题是后续新增PropertyType时需要持续修改分支逻辑,不符合开闭原则,代码会越来越臃肿,下面是两种可落地的优化方案:
方案1:字典映射委托(性能最优,推荐优先使用)
本质是策略模式的轻量化实现,将每个枚举对应的转换、赋值逻辑提前存入静态字典,程序初始化后直接调用,无额外运行时开销。
实现步骤
- 首先在当前类中定义静态只读的属性赋值字典,预存所有枚举对应的处理逻辑:
// 字典值为接收Enrichment实例和新值的委托,内置对应属性的赋值、转换逻辑 private static readonly IReadOnlyDictionary<PropertyType, Action<Enrichment, string>> _enrichmentPropertySetters = new Dictionary<PropertyType, Action<Enrichment, string>> { [PropertyType.Color] = (enrichment, newValue) => enrichment.Color = newValue, [PropertyType.UsedVehicleModel] = (enrichment, newValue) => enrichment.UsedModel = newValue, [PropertyType.UsedVehicleYear] = (enrichment, newValue) => enrichment.UsedYear = newValue, [PropertyType.UsedVehicleKm] = (enrichment, newValue) => enrichment.UsedKm = newValue, [PropertyType.Payment] = (enrichment, newValue) => enrichment.Payment = Convert.ToInt32(EnumExtension.GetEnumValueFromDescription<PaymentType>(newValue)), [PropertyType.ScheduleDate] = (enrichment, newValue) => enrichment.ScheduleDate = DateTime.ParseExact(newValue, "dd/MM/yyyy", CultureInfo.InvariantCulture), [PropertyType.SchedulePeriod] = (enrichment, newValue) => enrichment.SchedulePeriod = newValue, [PropertyType.SchedulePhone] = (enrichment, newValue) => enrichment.SchedulePhone = newValue, [PropertyType.PurchaseType] = (enrichment, newValue) => enrichment.PurchaseType = newValue, [PropertyType.HasOptInNextJeep] = (enrichment, newValue) => enrichment.HasOptInNextJeep = Convert.ToBoolean(newValue) };
- 替换原方法中的switch分支逻辑:
private async Task<Enrichment> ParseEnrichmentNewDataAsync(int leadId, string property, string newValue) { var enrichment = await _context.EnrichmentRepository.GetByLeadIdAsync(leadId); if (enrichment == null) { enrichment = new Enrichment() { LeadId = leadId }; } // 替换原switch代码块 if (Enum.TryParse<PropertyType>(property, out var enumProperty) && _enrichmentPropertySetters.TryGetValue(enumProperty, out var propertySetter)) { propertySetter(enrichment, newValue); } // 可自行扩展else分支,处理非法属性名、枚举未定义的异常场景 else { throw new ArgumentException($"不支持的属性名:{property}"); } return enrichment; }
方案优势
- 性能远高于反射实现,无运行时反射开销
- 符合开闭原则,后续新增属性仅需在字典中新增对应条目即可,无需修改业务逻辑
- 每个属性的处理逻辑内聚,调试、修改更方便
方案2:自定义特性+反射(适合属性量极大的场景)
如果你的PropertyType枚举数量很多,且大部分属性都是直接赋值不需要特殊转换,可以用这种方案减少重复代码。
实现步骤
- 定义自定义属性标记,以及值转换器接口:
// 用来标记枚举对应的Enrichment属性、可选的转换器 [AttributeUsage(AttributeTargets.Field)] public class EnrichmentPropertyAttribute : Attribute { public string TargetPropertyName { get; } public Type? ValueConverterType { get; } public EnrichmentPropertyAttribute(string targetPropertyName, Type? valueConverterType = null) { TargetPropertyName = targetPropertyName; ValueConverterType = valueConverterType; } } // 值转换器接口,特殊转换逻辑实现该接口即可 public interface IValueConverter { object Convert(string inputValue); }
- 为需要特殊转换的场景实现对应转换器:
// 支付类型转换器 public class PaymentConverter : IValueConverter { public object Convert(string inputValue) { return Convert.ToInt32(EnumExtension.GetEnumValueFromDescription<PaymentType>(inputValue)); } } // 预约日期转换器 public class ScheduleDateConverter : IValueConverter { public object Convert(string inputValue) { return DateTime.ParseExact(inputValue, "dd/MM/yyyy", CultureInfo.InvariantCulture); } } // 布尔值转换器 public class BooleanConverter : IValueConverter { public object Convert(string inputValue) { return Convert.ToBoolean(inputValue); } }
- 给
PropertyType枚举加特性标记:
public enum PropertyType { [EnrichmentProperty(nameof(Enrichment.Color))] Color, [EnrichmentProperty(nameof(Enrichment.UsedModel))] UsedVehicleModel, [EnrichmentProperty(nameof(Enrichment.UsedYear))] UsedVehicleYear, [EnrichmentProperty(nameof(Enrichment.UsedKm))] UsedVehicleKm, [EnrichmentProperty(nameof(Enrichment.Payment), typeof(PaymentConverter))] Payment, [EnrichmentProperty(nameof(Enrichment.ScheduleDate), typeof(ScheduleDateConverter))] ScheduleDate, [EnrichmentProperty(nameof(Enrichment.SchedulePeriod))] SchedulePeriod, [EnrichmentProperty(nameof(Enrichment.SchedulePhone))] SchedulePhone, [EnrichmentProperty(nameof(Enrichment.PurchaseType))] PurchaseType, [EnrichmentProperty(nameof(Enrichment.HasOptInNextJeep), typeof(BooleanConverter))] HasOptInNextJeep }
- 类初始化时构建枚举到属性的映射缓存(避免每次调用都反射),替换赋值逻辑:
// 静态缓存映射关系,仅初始化一次 private static readonly IReadOnlyDictionary<PropertyType, (PropertyInfo TargetProperty, IValueConverter? Converter)> _propertyMap = BuildPropertyMap(); private static IReadOnlyDictionary<PropertyType, (PropertyInfo, IValueConverter?)> BuildPropertyMap() { var map = new Dictionary<PropertyType, (PropertyInfo, IValueConverter?)>(); var enrichmentType = typeof(Enrichment); foreach (PropertyType enumValue in Enum.GetValues(typeof(PropertyType))) { var member = typeof(PropertyType).GetMember(enumValue.ToString()).First(); var attr = member.GetCustomAttribute<EnrichmentPropertyAttribute>(); if (attr == null) continue; var targetProp = enrichmentType.GetProperty(attr.TargetPropertyName)!; IValueConverter? converter = null; if (attr.ValueConverterType != null) { converter = (IValueConverter)Activator.CreateInstance(attr.ValueConverterType)!; } map[enumValue] = (targetProp, converter); } return map; } // 原方法中的赋值逻辑替换为: if (Enum.TryParse<PropertyType>(property, out var enumProperty) && _propertyMap.TryGetValue(enumProperty, out var mapItem)) { var valueToSet = mapItem.Converter != null ? mapItem.Converter.Convert(newValue) : newValue; mapItem.TargetProperty.SetValue(enrichment, valueToSet); }
方案优势
- 新增属性时仅需给枚举加一行特性即可,不需要重复写赋值代码,适合属性数量超过20个的场景
- 转换逻辑和属性定义耦合,一目了然,便于维护
选型建议
一般业务场景优先选方案1,性能更高、调试更简单,没有反射的心智负担;如果属性数量极多,且90%以上都是直接字符串赋值的场景,可以选方案2减少重复代码。
内容的提问来源于stack exchange,提问作者Matheus Barbosa
相关产品推荐
相关产品推荐

