如何在 Swift 中创建一个通用函数,它将拒绝给定的参数,除非它是可选的?
How to create a generic function in Swift that will reject the given parameter unless it is an Optional?
这个问题是我之前问题的后续问题:
Please read the referred question for you to get a better idea of the
constraints at hand.
我在 Swift 中创建了一个通用函数,它将拒绝其参数,除非这样的参数是 Optional
。我创建的功能完全可以工作并且可以满足我的要求。
意思是,任何对 onlyCallableByAnOptable(...)
的调用,即使在 if let
内部,也会由于不符合协议而产生错误,完全符合要求。
错误如:Argument type 'UIColor' does not conform to expected type 'Optable'
我唯一的问题是:有更简单的解决方案吗?
To make it clear: func onlyCallableWithAnOptinalParameter<T>(:T)->T
needs to work when called in an if let
statement like func test()
does.
protocol Optable {
associatedtype OptableType
func optionalOptable() -> OptableType?
}
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 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)")
}
}
}
您需要将传递给 onlyCallableByAnOptional
的 value
参数设为可选。同样,在该函数的 return 语句中,您还需要选择展开 value
以便它可以执行 optionalOptable
函数。
func onlyCallableByAnOptable<T>( _ value: T?) -> T.OptableType? where T: Optable {
return value?.optionalOptable()
}
你可能想给这个协议起一个更好的名字,但我预计它不会有任何问题,除非你正在制作自己的 ExpressibleByNilLiteral
不换行的类型。
protocol ExpressibleByNilLiteral: Swift.ExpressibleByNilLiteral {
associatedtype Wrapped
}
extension Optional: ExpressibleByNilLiteral { }
func onlyCallableByAnOptional<Optional: ExpressibleByNilLiteral>(_ optional: Optional) -> Optional.Wrapped? {
optional as? Optional.Wrapped
}
建议:使用初始化程序。 (缺点是参数标签是消除歧义所必需的,但我个人喜欢这种明确性,因为这种情况很奇怪。即 Swift 可以很容易地强制执行 不是 可选,反之则不然。)
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)")
}
以下是@Jessy的解决方案,基本上是我的解决方案的简化。
干得好,杰西。
我决定在这里用different/simpler重写它,希望通用类型和协议"confusing"的名称更少,为了更易读,让新手容易混淆,也更接近我问题中使用的名称。
If anyone happens to know of an even more elegant approach, you are very welcome to post it.
protocol Optable {
associatedtype Wrapped
}
extension Optional: Optable { }
func onlyCallableByAnOptable<T>(_ value: T) -> T.Wrapped? where T: Optable {
return value as? T.Wrapped
}
或者,如果您碰巧更喜欢 Jessy 的使用初始化程序的解决方案,这里是重命名的版本:
protocol Optable {
associatedtype Wrapped
}
extension Optional: Optable {
init<T: Optable>(optional o: T) where T.Wrapped == Wrapped {
self = o as? Wrapped
}
}
这个问题是我之前问题的后续问题:
Please read the referred question for you to get a better idea of the constraints at hand.
我在 Swift 中创建了一个通用函数,它将拒绝其参数,除非这样的参数是 Optional
。我创建的功能完全可以工作并且可以满足我的要求。
意思是,任何对 onlyCallableByAnOptable(...)
的调用,即使在 if let
内部,也会由于不符合协议而产生错误,完全符合要求。
错误如:Argument type 'UIColor' does not conform to expected type 'Optable'
我唯一的问题是:有更简单的解决方案吗?
To make it clear:
func onlyCallableWithAnOptinalParameter<T>(:T)->T
needs to work when called in anif let
statement likefunc test()
does.
protocol Optable {
associatedtype OptableType
func optionalOptable() -> OptableType?
}
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 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)")
}
}
}
您需要将传递给 onlyCallableByAnOptional
的 value
参数设为可选。同样,在该函数的 return 语句中,您还需要选择展开 value
以便它可以执行 optionalOptable
函数。
func onlyCallableByAnOptable<T>( _ value: T?) -> T.OptableType? where T: Optable {
return value?.optionalOptable()
}
你可能想给这个协议起一个更好的名字,但我预计它不会有任何问题,除非你正在制作自己的 ExpressibleByNilLiteral
不换行的类型。
protocol ExpressibleByNilLiteral: Swift.ExpressibleByNilLiteral {
associatedtype Wrapped
}
extension Optional: ExpressibleByNilLiteral { }
func onlyCallableByAnOptional<Optional: ExpressibleByNilLiteral>(_ optional: Optional) -> Optional.Wrapped? {
optional as? Optional.Wrapped
}
建议:使用初始化程序。 (缺点是参数标签是消除歧义所必需的,但我个人喜欢这种明确性,因为这种情况很奇怪。即 Swift 可以很容易地强制执行 不是 可选,反之则不然。)
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)")
}
以下是@Jessy的解决方案,基本上是我的解决方案的简化。
干得好,杰西。
我决定在这里用different/simpler重写它,希望通用类型和协议"confusing"的名称更少,为了更易读,让新手容易混淆,也更接近我问题中使用的名称。
If anyone happens to know of an even more elegant approach, you are very welcome to post it.
protocol Optable {
associatedtype Wrapped
}
extension Optional: Optable { }
func onlyCallableByAnOptable<T>(_ value: T) -> T.Wrapped? where T: Optable {
return value as? T.Wrapped
}
或者,如果您碰巧更喜欢 Jessy 的使用初始化程序的解决方案,这里是重命名的版本:
protocol Optable {
associatedtype Wrapped
}
extension Optional: Optable {
init<T: Optable>(optional o: T) where T.Wrapped == Wrapped {
self = o as? Wrapped
}
}