如何在Swift中创建一个通用函数,除非它是可选参数,否则它将拒绝给定参数?

问题描述 投票:0回答:3

此问题是我先前问题的跟进: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)->Tif 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)") 
        }
    }
}
swift optional type-inference swift-protocols swift-optionals
3个回答
1
投票

您可能想给这个协议起一个更好的名字,但是我不认为它会带来任何问题,除非您要制作自己的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)") }


0
投票
您需要将传递给valueonlyCallableByAnOptional参数设为可选。同样,在该函数的return语句上,您还需要选择解开value,以便它可以执行optionalOptable函数。

func onlyCallableByAnOptable<T>( _ value: T?) -> T.OptableType? where T: Optable { return value?.optionalOptable() }


0
投票
以下是

@ 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 }

© www.soinside.com 2019 - 2024. All rights reserved.