允许 Swift 函数参数为多种类型

Allow Swift function parameter to be of multiple types

一个函数

func stepperValueChanged(_ myStepper: UIStepper) {
    // do stuff with myStepper
}

第二个函数

func switchValueChanged(_ mySwitch: UISwitch) {
    // do stuff with mySwitch
}

如何创建第三个(替代)函数,它可以采用任何一种类型?

func valueChanged(_ myComponent: /* ??? UIStepper or UISwitch, but nothing else ??? */) {
    // do stuff with myComponent
}

我探索过使用枚举、类型别名和协议;这导致了很多有趣的 Whosebug 阅读,但没有解决方案。

无效的例子

// ** DON'T COPY AND PASTE, DONT WORK!! ** //
typealias validUIComponent = UIStepper, UISwitch
// or
typealias validUIComponent = UIStepper & UISwitch
// or
enum UIComponent { case stepper(UIStepper); case _switch(UISwitch) }
// or
protocol UIComponent { }
extension UIStepper: UIComponent { }
extension UISwitch: UIComponent { }
// ** DON'T COPY AND PASTE, DONT WORK!! ** //

我为什么要这样做?类型检查。我不想将任何其他 UI 元素传递给该函数。

我意识到我可以 if let/guard let 或某种其他形式在函数体中检查一次并根据需要退出,但这只会捕获 运行-time 而不是编译时类型错误.

我也意识到我可以使用 Any?或(更好)UI根据需要进行控制和向下转换。

func valueChanged(_ myComponent: UIControl) {
    // do stuff with
    myComponent as! UIStepper
    // do stuff with
    myComponent as! UISwitch
}

但是有syntactic/more表达的解决方案吗?

您提到了枚举,这听起来非常适合此用例。 您可以明确地只要求您期望的类型,而不是其他任何类型。 通过将 属性 添加到枚举中,您可以公开 UIControl 属性 以根据需要进行交互,而无需向下转换(这通常被认为是一种反模式) .

enum Component {
  case `switch`(UISwitch)
  case stepper(UIStepper)
  
  var control: UIControl {
    switch self {
      case .switch(let comp):
        return comp
      case .stepper(let comp):
        return comp
    }
  }
}

然后请求一个Component作为函数的参数。

func controlValueChanged(_ myComponent: Component) {
  // Now you can use them as a generic UIControl
  let control = myComponent.control
  
  // ...or different behaviours for each element
  switch myComponent {
    case .switch(let swit):
      // use the `swit`
    case .stepper(let step):
      // use the `step`
  }
}

话虽如此,如果这些类型的实现无论如何都完全不同,那么定义两个单独的函数可能会更清楚。

我认为你在这里把事情复杂化了,没有必要通过引入协议或新类型(枚举)来创建任何开销。

相反,我会通过使用多态性并声明具有相同签名的多个函数来处理这个问题。

func controlValueChanged(_ switch: UISwitch) {
    // code…
}

func controlValueChanged(_ stepper: UIStepper) {
    // code…
}

这将使调用代码保持整洁,每个函数的职责清晰。

如果两个函数之间有一些通用代码,则将其提取到第三个通用函数中