简体   繁体   中英

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.

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.

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.

Also I realise I could use Any? or (better) UIControl and downcast as needed.

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).

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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