简体   繁体   English

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

[英]Allow Swift function parameter to be of multiple types

A function一个函数

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

A second function第二个功能

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

How do I create a third (alternative) function that can take either type?如何创建可以采用任一类型的第三个(替代)函数?

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

I've explored using enums, typealiases and protocols;我已经探索过使用枚举、类型别名和协议; which resulted in lots of interesting Stackoverflow reads but no solution.这导致了许多有趣的 Stackoverflow 读取,但没有解决方案。

Examples that don't work不起作用的例子

// ** 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!! ** //

Why would I want to do this?我为什么要这样做? Type checking.类型检查。 I don't want any other UI element to be passed to the function.我不希望将任何其他 UI 元素传递给该函数。

I realise I could if let/guard let or some other form of checking once in the function body and bail as required, but this would only catch run-time not compile-time type errors.我意识到如果 let/guard let 或某种其他形式的检查一次在函数体中并根据需要保释我可以,但这只会捕获运行时而不是编译时类型错误。

Also I realise I could use Any?我也意识到我可以使用任何? or (better) UIControl and downcast as needed.或(更好) UIControl 并根据需要向下转型。

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

But is there a syntactic/more expressive solution?但是有没有语法/更具表现力的解决方案?

You mentioned enums, which sound like an excellent fit for this use case.你提到了枚举,这听起来非常适合这个用例。 You can explicitly only require the types you expect, and nothing else.您可以明确地只要求您期望的类型,而没有其他要求。 By adding a property to the enum you can then expose a UIControl property to interact as needed, without the need for down-casting (which is generally considered to be an anti-pattern).通过向枚举添加属性,您可以公开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
    }
  }
}

Then ask for a Component as a parameter to the function.然后请求一个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`
  }
}

Having said that, if the implementations for these types are totally different anyway, it may be more clear to define two separate functions.话虽如此,如果这些类型的实现无论如何都完全不同,那么定义两个单独的函数可能会更清楚。

I think you are over complicating things here and that there is no need to create any overhead by introducing a protocol or a new type (enum).我认为你在这里把事情复杂化了,并且没有必要通过引入协议或新类型(枚举)来创建任何开销。

Instead I would handle this by using polymorphism and declaring multiple functions with the same signature.相反,我会通过使用多态并声明多个具有相同签名的函数来处理这个问题。

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

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

This will keep the calling code nice and tidy and the responsibility of each function clear.这将使调用代码保持整洁,并使每个函数的责任清晰。

If there are some common code between the two functions then extract that into a third common function如果两个函数之间有一些公共代码,则将其提取到第三个公共函数中

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM