在Swift中实现关闭的协议

时间:2014-07-04 17:37:09

标签: types enums swift prototype

我想在我的Swift项目中创建一些函数,它们可以接受一个对象,或一个返回该对象类型的闭包。我当然可以在每个地方定义具有多个签名的相同功能,但这很详细。我也希望能够创建这些对象/对象返回闭包的类型安全列表,但是如果没有描述这两种东西的常见类型,就不能这样做。

这就是我想做的事情

typealias StringClosure = () -> String

protocol Stringable {
    func toStringClosure() -> StringClosure
}

extension String : Stringable {
    func toStringClosure() -> StringClosure {
        return { return self }
    }
}

extension StringClosure : Stringable {
    func toStringClosure() -> StringClosure {
        return self
    }
}

func printStringable(a : Stringable) {
    print(a.toStringClosure()())
}


var stringableList : Stringable[] = ["cat", {return "dog"}, "gecko"]

for stringable in StringableList {
    printStringable(stringable)
}

但这不起作用,因为我实际上无法扩展StringClosure类型来实现Stringable。我可以stringableList列出Any类型,但这不是类型安全的。

枚举解决方案

一个解决方案是我可以创建一个枚举类型,但这意味着我必须明确注释我使用这些类型的枚举,这是蹩脚的。这看起来像这样:

enum StringableEnum {
    case Str(String)
    case Fun(StringClosure)
}

func printStringableEnum(a : StringableEnum) {
    switch (a) {
    case let .Str(value):
        print(value)
    case let .Fun(value):
        print(value())
    }
}

var enumList : StringableEnum[] = [.Str("cat"), .Fun({return "dog"}), .Str("gecko")]

for element in enumList {
    printStringableEnum(element)
}

它还不错,但它要求我的API用户现在知道这个枚举,并在每次调用{{1}时用.Str.Fun标记其参数功能。不是一个很好的API!

这可能对语言的要求太高,但有没有人有更好的想法?

3 个答案:

答案 0 :(得分:2)

我认为在这种情况下,最简单的解决方案可能是最好的。不要让字符串直接传入,而是需要闭包。将字符串转换为闭包不是很难:

let myString = "Hello"
printStringable({return  myString})

为方便起见,您甚至可以创建一个将值转换为闭包的函数:

func f<T>(value : T) -> () -> T  {
    return {return value}
}

printStringable(f("Hello"))
printStringable(f(myString))

虽然我认为保存的少数字符不值得这个可能令人困惑的函数名称。

修改

你也可以这样改进你的枚举:

enum StringableEnum {
    case Str(String)
    case Fun(() -> String)

    init(_ string : String) {
        self = .Str(string)
    }

    init(_ closure : () -> String) {
        self = .Fun(closure)
    }

    var value : String {
        switch(self) {
            case let .Str(value):
                return value
            case let .Fun(closure):
                return closure()
        }
    }
}

这意味着您可以从任何支持的类型创建枚举,如下所示:

var stringable = StringableEnum("Hello")
stringable = StringableEnum({return "Hello"})

你可以通过

获取字符串
stringable.value

答案 1 :(得分:2)

编辑:请勿执行以下操作。它适用于Beta 4,但依赖于Apple不希望公众使用并打算在Swift 1.0发布之前删除的功能。


@auto_closure可能适合您,但问题在于您似乎无法将显式闭包传递给@auto_closure参数。

另一种方法是将所有内容视为闭包,并使用Swift的__conversion功能在需要时隐式将字符串转换为StringClosures:

typealias StringClosure = () -> String

extension String {
    func __conversion() -> StringClosure {
        return { self } // 'return' can be omitted inside a single-expression closure
    }
}

// type inferencing automatically figures out that
// stringableList should be [StringClosure] and applies
// the conversion method to promote the string entries into
// self-returning closures
var stringableList = ["cat", { println("side-effects are bad"); return "dog"}, "gecko"]

func printStringClosure(s: StringClosure) {
    println(s())
}

for s in stringableList {
    printStringClosure(s)
}

printStringClosure("test")
printStringClosure { let a = 5*5; return "\(a)" }

答案 2 :(得分:0)

不要忘记swift可以选择创建像这样的延迟计算变量(它们不限于类/结构):

var lazy : String {
    return "Hello"
}

printStringable(lazy)

如果您不需要参数化闭包,这是一个更简单的解决方案,可以解决具有副作用问题的惰性计算/闭包。