如何定义一个可以保存一组特定闭包签名的变量?

时间:2018-03-17 05:19:41

标签: swift pattern-matching swift4

考虑以下代码。它允许您通过传入可以处理有效负载的闭包来实例化处理器。闭包可以是六种签名类型之一。以下是我实施的方法:

我想知道是否有更简单/更好的方式来写这个。我想也许通过使用带有关联值的枚举,但这意味着我必须将闭包包装在枚举中,然后将枚举传递给初始化器。我希望尽可能避免这种情况。

struct Payload {
    let stringVal : String
    let intVal    : Int
}

class Processor{

    init(action:Any){

        switch action {
            case let action as ()       -> Void,
                 let action as ()       -> Error?,
                 let action as (String) -> Void,
                 let action as (String) -> Error?,
                 let action as (Int)    -> Void,
            case let action as (Int)    -> Error?,
            default: fatalError("Action does not have a supported signature")
        }

        self.action = action
    }

    let action:Any

    func performAction(payload:Payload) -> Error? {

        switch action {

            case let action as () -> Void:
                action()

            case let action as () -> Error?:
                return action()

            case let action as (String) -> Void:
                action(payload.stringVal)

            case let action as (String) -> Error?:
                return action(payload.stringVal)

            case let action as (Int) -> Void:
                action(payload.intVal)

            case let action as (Int) -> Error?:
                return action(payload.intVal)

            default: fatalError("Action does not have a supported signature")
        }

        return nil
    }
}

此外,在初始化程序中,我正在执行模式匹配以确定我是否有有效匹配,我收到编译器警告说我已设置,但从未使用过action。但是,如果我用_替换它,那么我会得到一个警告,我没有绑定到模式中的任何内容。如何在对我不关心值的模式执行测试的同时抑制警告?

1 个答案:

答案 0 :(得分:0)

避免使用Any类型的参数。使用带有相关类型的枚举:

enum Action {
    case a1(() -> Void)
    case a2(() -> Error?)
    case a3((String) -> Void)
    case a4((String) -> Error?)
    case a5((Int) -> Void)
    case a6((Int) -> Error?)
}

struct Payload {
    let stringVal : String
    let intVal    : Int
}

class Processor{
    let action: Action

    init(action: Action) {
        self.action = action
    }

    func performAction(payload: Payload) -> Error? {
        var error: Error? = nil
        switch action {
        case let .a1(closure):
            closure()
        case let .a2(closure):
            error = closure()
        case let .a3(closure):
            closure(payload.stringVal)
        case let .a4(closure):
            error = closure(payload.stringVal)
        case let .a5(closure):
            closure(payload.intVal)
        case let .a6(closure):
            error = closure(payload.intVal)
        }

        return error
    }
}

这可确保您只能将有效操作传递到Payload类。

Action案例值需要有更好的命名约定,但这会让你开始。

以下是用法示例:

let payload = Payload(stringVal: "Hello", intVal: 42)
let myAction = {
    print("Working")
}
let action = Action.a1(myAction)
let processor = Processor(action: action)
let error = processor.performAction(payload: payload)

另一种方法是将Action置于Processor内部,并为Processor提供6个初始值设定项。

struct Payload {
    let stringVal : String
    let intVal    : Int
}

class Processor{
    enum Action {
        case a1(() -> Void)
        case a2(() -> Error?)
        case a3((String) -> Void)
        case a4((String) -> Error?)
        case a5((Int) -> Void)
        case a6((Int) -> Error?)
    }

    private let action: Action

    init(_ closure: @escaping () -> Void) {
        self.action = .a1(closure)
    }

    init(_ closure: @escaping () -> Error?) {
        self.action = .a2(closure)
    }

    init(_ closure: @escaping (String) -> Void) {
        self.action = .a3(closure)
    }

    init(_ closure: @escaping (String) -> Error?) {
        self.action = .a4(closure)
    }

    init(_ closure: @escaping (Int) -> Void) {
        self.action = .a5(closure)
    }

    init(_ closure: @escaping (Int) -> Error?) {
        self.action = .a6(closure)
    }

    func performAction(payload: Payload) -> Error? {
        var error: Error? = nil
        switch action {
        case let .a1(closure):
            closure()
        case let .a2(closure):
            error = closure()
        case let .a3(closure):
            closure(payload.stringVal)
        case let .a4(closure):
            error = closure(payload.stringVal)
        case let .a5(closure):
            closure(payload.intVal)
        case let .a6(closure):
            error = closure(payload.intVal)
        }

        return error
    }
}

let payload = Payload(stringVal: "Hello", intVal: 42)
let myAction = {
    print("Working")
}
let processor = Processor(myAction)
let error = processor.performAction(payload: payload)