Swift中能否将属性名作为参数传入函数以避免代码重复?
问题解答:Swift中复用统计属性的实现方式
问题描述
我定义了如下的Stats类和Stat结构体:
class Stats { var stats: [Stat] } struct Stat { var id: UUID var countA: Int var countB: Int var countC: Int var totalAAttempts: Int var totalBAttempts: Int var totalCAttempts: Int // 其他属性 }
目前在Stats类中编写了大量重复的计算属性来统计百分比并格式化显示:
class Stats { var stats: [Stat] var countAPerc: Double { let totalA = stats.reduce(0) { total, stat in total + stat.countA } let totalAttempts = stats.reduce(0) { total, stat in total + stat.totalAAttempts } return Double(totalA) / Double(totalAttempts) } var countAPercDisplay: String { String(format: "%.1f", countAPerc * 100) + "%" } var countBPerc: Double { let totalB = stats.reduce(0) { total, stat in total + stat.countB } let totalAttempts = stats.reduce(0) { total, stat in total + stat.totalBAttempts } return Double(totalB) / Double(totalAttempts) } var countBPercDisplay: String { String(format: "%.1f", countBPerc * 100) + "%" } // 更多重复的计算属性 }
我希望通过将属性名作为参数传入函数的方式避免重复代码,比如编写如下复用函数:
func summarizeProperty(_ countProperty: PROPERTYNAME1, _ totalProperty: PROPERTYNAME2) -> Double { let totalCount = stats.reduce(0) { total, stat in total + stat.PROPERTYNAME1 } let totalAttempts = stats.reduce(0) { total, stat in total + stat.PROPERTYNAME2 } return Double(totalCount) / Double(totalAttempts) } func summarizedProperty(_ countProperty: PROPERTYNAME1, _ totalProperty: PROPERTYNAME2) -> String { let summarized = summarizeProperty(PROPERTYNAME1, PROPERTYNAME2) return String(format: "%.1f", summarized * 100) + "%" }
并通过Text(summarizedProperty(countA, totalAAttempts))这样的方式调用,请问这种实现方式是否可行?
可行的实现方案
你想要的复用思路是对的,但直接传属性名在Swift中无法实现,需要用**键路径(KeyPath)**来传递属性的引用,这是Swift中复用属性访问逻辑的标准方式。
修改后的Stats类可以这样实现:
class Stats { var stats: [Stat] // 计算百分比的复用方法,接收两个KeyPath参数 func summarizeProperty(countKey: KeyPath<Stat, Int>, totalKey: KeyPath<Stat, Int>) -> Double { let totalCount = stats.reduce(0) { $0 + $1[keyPath: countKey] } let totalAttempts = stats.reduce(0) { $0 + $1[keyPath: totalKey] } // 避免除以0的情况,防止出现NaN guard totalAttempts != 0 else { return 0 } return Double(totalCount) / Double(totalAttempts) } // 格式化显示的复用方法 func summarizedPropertyDisplay(countKey: KeyPath<Stat, Int>, totalKey: KeyPath<Stat, Int>) -> String { let percentage = summarizeProperty(countKey: countKey, totalKey: totalKey) return String(format: "%.1f", percentage * 100) + "%" } // 可选:保留原有计算属性,内部调用复用方法,兼容旧代码 var countAPercDisplay: String { summarizedPropertyDisplay(countKey: \.countA, totalKey: \.totalAAttempts) } }
调用方式如下:
let stats = Stats(stats: [...]) // 初始化Stats实例 Text(stats.summarizedPropertyDisplay(countKey: \.countA, totalKey: \.totalAAttempts)) Text(stats.summarizedPropertyDisplay(countKey: \.countB, totalKey: \.totalBAttempts))
关键点说明
- 键路径(KeyPath):
\.countA是Stat结构体中countA属性的键路径,通过$1[keyPath: countKey]可以访问对应属性的值,完美实现属性引用的传递。 - 健壮性处理:增加
guard语句防止总尝试数为0时出现除以0的错误,避免返回NaN。 - 兼容性:如果不想修改原有外部调用代码,可以保留原来的计算属性,内部复用新方法,实现无缝过渡。
内容的提问来源于stack exchange,提问作者Dovahkiin
相关产品推荐
相关产品推荐

