How to detect that parameter is a tuple of two arbitrary types?

本秂侑毒 提交于 2019-12-18 13:36:30

问题


What I am actually doing is more complex but it comes down to being able to implement function to detect that something is a tuple, regardless of what the are the types of its elements.

This is my approch that does not work (see comment on last line) :

func isTuple(b: Any) -> Bool {
    return b is (Any, Any)
}

let myString = "aa"
let myDouble = 1.2
isTuple((myString, myDouble)) //returns false

Why doesn't it work? Shouln't Any act as a "wildcard" in tuples as well? Is it a known Swift bug (if not should I consider it one and report)? Is there other way how to make isTupple method work?


EDIT

@NateCook's answer fully answers original question but does it does not help me to do what I am trying to do. This is it:

I need not only to determine that something is a tupple but also to break it into its 2 values without knowing the exact type of those values.

Here is code:

func processIfTuple(b: Any) {
    if reflect(b).disposition == MirrorDisposition.Tuple {
        let (first, second) = b as (Any, Any) //error when casting
        process(first)
        process(second)
    }
}

func process(value: Any) {
    ...
}

processIfTuple(("aa", 1.2))

This does not work from similar reason that the test b is (Any, Any) does not work. This time there is error when trying to cast. Can this be solved? If not should it be considered language bug or missing feature and reported? It definitely leads to a LOT of code duplication because I need to test for all possible pairs of types for a tuple.


回答1:


You can use Swift's baby introspection methods to get at this:

func isTuple(b: Any) -> Bool {
    return reflect(b).disposition == MirrorDisposition.Tuple
}

Note that reflect is largely undocumented and may only be there as support for the playground / debugger, but as far as I know this is the only way to do this.


To achieve this you need to drill down into what reflect() gives you, which is a struct that conforms to MirrorType, which I call a reflection, for lack of a better term. You can subscript the reflection of a tuple to get reflections of the tuples members, and then get the value back out as Any. At that point you can use optional binding to safely rediscover the underlying type:

func process(value: Any) {
    println("Any \(value)")
}

func process(value: String) {
    println("String \(value)")
}

func processTuple(b: Any) -> Bool {
    let isTuple = reflect(b).disposition == MirrorDisposition.Tuple

    let r = reflect(b)
    for i in 0..<r.count {
        println(r[i].0)         // string holding tuple part name: ".0", ".1", etc
        println(r[i].1.value)   // the value of that tuple part: "aa", 1.2

        process(r[i].1.value)   // calls process(Any)
        if let val = r[i].1.value as? String {
            process(val)        // calls process(String)
        }
    }

    return isTuple
}

let myString = "aa"
let myDouble = 1.2
processTuple((myString, myDouble)) //returns false

Output:

.0
aa
Any aa
String aa
.1
1.2
Any 1.2



回答2:


In Swift 3, it's now done this way:

func isTuple(value: Any) -> Bool {
    if let type = Mirror(reflecting: value).displayStyle, type == .tuple {
        return true
    }

    return false
}

isTuple(value: ()) // true
isTuple(value: (1, 2) // true
isTuple(value: 3) // false


来源:https://stackoverflow.com/questions/27132761/how-to-detect-that-parameter-is-a-tuple-of-two-arbitrary-types

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!