我想在我的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!
这可能对语言的要求太高,但有没有人有更好的想法?
答案 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)
如果您不需要参数化闭包,这是一个更简单的解决方案,可以解决具有副作用问题的惰性计算/闭包。