简体   繁体   English

如何在 Swift 中创建一个通用函数来拒绝给定的参数,除非它是一个 Optional?

[英]How to create a generic function in Swift that will reject the given parameter unless it is an Optional?

This question is a follow up to my earlier question: I expected the system to report non protocol conformance, but it does not!这个问题是我之前问题的后续: 我希望系统报告不符合协议的情况,但它没有! Why? 为什么?

Please read the referred question for you to get a better idea of the constraints at hand.请阅读所提及的问题,以更好地了解手头的限制。

I created a generic function in Swift that will reject its parameter unless such a parameter is Optional .我在 Swift 中创建了一个通用函数,它将拒绝它的参数,除非这样的参数是Optional The function I created fully works and does what I desire.我创建的功能完全可以工作并且可以满足我的愿望。

Meaning, any calls to onlyCallableByAnOptable(...) , even inside an if let , will yield error due to non-protocol conformance, exactly as desired .意思是,任何对onlyCallableByAnOptable(...)调用,即使在if let ,也会由于非协议一致性而产生错误,完全符合预期

Errors like: Argument type 'UIColor' does not conform to expected type 'Optable'错误如: Argument type 'UIColor' does not conform to expected type 'Optable'

My only question is: Is there a simpler solution?我唯一的问题是:有没有更简单的解决方案?

To make it clear: func onlyCallableWithAnOptinalParameter<T>(:T)->T needs to work when called in an if let statement like func test() does.明确地说: func onlyCallableWithAnOptinalParameter<T>(:T)->T在像func test()这样的if let语句中调用时需要工作。

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)") 
        }
    }
}

You might want to give this protocol a better name, but I don't foresee any problems with it as-is, unless you're making your own ExpressibleByNilLiteral types that don't wrap.你可能想给这个协议一个更好的名字,但我不认为它有任何问题,除非你正在制作自己的不包装的ExpressibleByNilLiteral类型。

protocol ExpressibleByNilLiteral: Swift.ExpressibleByNilLiteral {
  associatedtype Wrapped
}

extension Optional: ExpressibleByNilLiteral { }

func onlyCallableByAnOptional<Optional: ExpressibleByNilLiteral>(_ optional: Optional) -> Optional.Wrapped? {
  optional as? Optional.Wrapped
}

Recommendation: use an initializer.建议:使用初始化程序。 (Downside is the argument label being necessary to disambiguate, but I personally like the explicitness because of how weird this case is. ie Swift makes it easy to enforce that something is not optional, but not vice versa.) (缺点是参数标签是消除歧义所必需的,但我个人喜欢这种明确性,因为这种情况有多么奇怪。即 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)")
}

You need to make the value parameter you pass into onlyCallableByAnOptional optional.您需要将传递给onlyCallableByAnOptionalvalue参数onlyCallableByAnOptional可选。 Likewise, on the return statement of that function, you will also need to optionally unwrap value so that it can execute the optionalOptable function.同样,在该函数的 return 语句中,您还需要选择性地解包value以便它可以执行optionalOptable函数。

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

The following is @Jessy's solution, which is basically a simplification of my solution.以下是@Jessy 的解决方案,基本上是我的解决方案的简化。

Well done Jessy.杰西干得好。

I decided to rewrite it here with different/simpler, hopefully, less "confusing" names for the generic type and the protocol , to make it more readable, let prone to confusion by newbies, and also more similar to the names used in my question.我决定在这里用不同/更简单的方式重写它,希望能减少泛型类型协议的“混淆”名称,使其更具可读性,让新手容易混淆,并且更类似于我的问题中使用的名称.

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
}

OR, if you happen to prefer Jessy's solution which uses an initializer, here is a renamed version:或者,如果您碰巧更喜欢使用初始化程序的 Jessy 解决方案,这里有一个重命名的版本:

protocol Optable {
    associatedtype Wrapped
}

extension Optional: Optable {
    init<T: Optable>(optional o: T) where T.Wrapped == Wrapped {
        self = o as? Wrapped
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM