此问题是我先前问题的跟进:I expected the system to report non protocol conformance, but it does not! Why?
请阅读您所提到的问题,以更好地了解制约因素。
我在Swift中创建了一个通用函数,除非该参数为Optional
,否则它将拒绝其参数。
即使在onlyCallableByAnOptable(...)
内部,对if let
的任何调用也将由于完全不符合协议而产生错误。
错误:Argument type 'UIColor' does not conform to expected type 'Optable'
我的问题是:有没有更简单的解决方案?
为了清楚起见:
func onlyCallableWithAnOptinalParameter<T>(:T)->T
在if let
语句中调用时需要像func test()
一样工作。
protocol Optable {
associatedtype OptableType
func optionalOptable() -> OptableType?
func opt()
}
func onlyCallableByAnOptable<T>( _ value: T) -> T.OptableType? where T: Optable {
return value.optionalOptable()
}
extension Optional: Optable {
typealias OptableType = Wrapped //: Wrapped is the type of the element, as defined in Optional
func opt() {}
func optionalOptable() -> OptableType? {
return self
}
}
class TestOptable {
static func test()
{
let c = UIColor.blue
let s = "hi"
let i = Int(10)
let oi: Int? = 10
if let v = onlyCallableByAnOptable(c) { // ERROR, as was desired.
print("color \(v)")
}
if let v = onlyCallableByAnOptable(s) { // ERROR, as was desired.
print("string \(v)")
}
if let v = onlyCallableByAnOptable(i) { // ERROR, as was desired.
print("integer \(v)")
}
if let v = onlyCallableByAnOptable(oi) { // OK, as expected.
print("optional integer \(v)")
}
}
}
您可能想给这个协议起一个更好的名字,但是我不认为它会带来任何问题,除非您要制作自己的ExpressibleByNilLiteral
类型而不包装。
protocol ExpressibleByNilLiteral: Swift.ExpressibleByNilLiteral {
associatedtype Wrapped
}
extension Optional: ExpressibleByNilLiteral { }
func onlyCallableByAnOptional<Optional: ExpressibleByNilLiteral>(_ optional: Optional) -> Optional.Wrapped? {
optional as? Optional.Wrapped
}
建议:使用初始化程序。 (缺点是要消除歧义是必要的参数标签,但是我个人喜欢明确性,因为这种情况很奇怪。即,Swift可以很容易地强制某些事情是[[not可选的,反之则不是)。]
extension Optional: ExpressibleByNilLiteral {
init<Optional: ExpressibleByNilLiteral>(optional: Optional) where Optional.Wrapped == Wrapped {
self = optional as? Wrapped
}
}
+
if let v = Optional(optional: i) { // ERROR, as was desired. print("integer \(v)") } if let v = Optional(optional: oi) { // OK, as expected. print("optional integer \(v)") }
value
的onlyCallableByAnOptional
参数设为可选。同样,在该函数的return语句上,您还需要选择解开value
,以便它可以执行optionalOptable
函数。func onlyCallableByAnOptable<T>( _ value: T?) -> T.OptableType? where T: Optable {
return value?.optionalOptable()
}
@ Jessy's解决方案,这基本上是我的解决方案的简化,并且在使用ExpressibleByNilLiteral
时也使用了@ Matt's技巧。
我决定在这里用不同/更简单的方式重写它,希望对
generic type
和protocol使用较少的“混淆”名称,以使其更具可读性,并容易引起新手的混淆,并且也更类似于我的问题中使用的名称。[如果有人碰巧知道更优雅的方法,欢迎您发布它。
protocol Optable: ExpressibleByNilLiteral {
associatedtype Wrapped
}
extension Optional: Optable { }
func onlyCallableByAnOptable<T>(_ value: T) -> T.Wrapped? where T: Optable {
return value as? T.Wrapped
}