Switch statement for imported NS_OPTIONS (RawOptionSetType) in Swift?

后端 未结 4 1652
既然无缘
既然无缘 2021-01-21 12:26

The switch statement in Swift is so much more expressive. I\'m wondering if this might be possible:

Lets look at UIViewAutoresizing for example. It\'s defined in Objecti

相关标签:
4条回答
  • 2021-01-21 13:16

    Another solution that I came up with is this:

    let foo = /* .. your value to test .. */
    
    let allCases = [UIViewAutoresizing.FlexibleHeight, UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleTopMargin]
    
    for oneCase in allCases {
        switch oneCase & foo {
    
        case UIViewAutoresizing.FlexibleHeight:
            println("height")
    
        case UIViewAutoresizing.FlexibleWidth:
            println("width")
    
        case UIViewAutoresizing.FlexibleTopMargin:
            println("top")
    
        default:
            break
        }
    }
    
    0 讨论(0)
  • 2021-01-21 13:18

    I tried several hours yesterday and today to make this work with switch — no success.

    The reason is that in this particular case we need to test against several cases. In Swift we need to use the fallthrough key word. But we are not allowed to fall through to the next case if that next case uses a variable, there for we cannot use the case let where statement, as shown here:

    switch foo {
    case let x where x & .FlexibleHeight != nil:
        println("height")
    case let x where x & .FlexibleWidth != nil:
        println("width")
    case let x where x & .FlexibleTopMargin != nil:
        println("top margin")
    default:
        println("no")
    }
    

    This will break out once a case triggered. But

    switch foo {
    case let x where x & .FlexibleHeight != nil:
        println("height")
        fallthrough
    
    case let x where x & .FlexibleWidth != nil:
        println("width")
        fallthrough
    
    case let x where x & .FlexibleTopMargin != nil:
        println("top margin")
    default:
        println("no")
    }
    

    does not work for the reason described above.


    I'd go with a clear if statement, like

    let foo = UIViewAutoresizing.FlexibleLeftMargin | UIViewAutoresizing.FlexibleHeight
    
    let width = foo & UIViewAutoresizing.FlexibleWidth;
    let height = foo & UIViewAutoresizing.FlexibleHeight;
    
    if width == .FlexibleWidth {
        println("width")
    }
    
    if height == .FlexibleHeight {
        println("height")
    }
    

    or

    let foo = UIViewAutoresizing.FlexibleLeftMargin | UIViewAutoresizing.FlexibleHeight
    
    let usesFlexibleWidth = (foo & UIViewAutoresizing.FlexibleWidth) != nil;
    let usesFlexibleHeight = (foo & UIViewAutoresizing.FlexibleHeight) != nil;
    
    if usesFlexibleWidth {
        println("width")
    }
    
    if usesFlexibleHeight {
        println("height")
    }
    
    0 讨论(0)
  • 2021-01-21 13:18

    You definitely can, although it's a little more convoluted now that RawOptionSetType doesn't implement the BooleanType protocol.

    switch foo {
    case let x where x & .FlexibleHeight != nil:
        println("height")
    case let x where x & .FlexibleWidth != nil:
        println("width")
    case let x where x & .FlexibleTopMargin != nil:
        println("top margin")
    default:
        println("no")
    }
    

    Note that this stops on the first condition that matches! So it really is another form of this:

    if foo & .FlexibleHeight != nil {
        println("height")
    } else if foo & .FlexibleWidth != nil {
        println("width")
    } else if foo & .FlexibleTopMargin != nil {
        println("top margin")
    }
    
    0 讨论(0)
  • 2021-01-21 13:19

    I was frustrated enough about this problem that I wrote a Bitmask<T> class that can handle these use cases. The code is up on Github: brynbellomy/SwiftBitmask

    It allows you to do stuff like this with any kind of object as your underlying type (here I'm using an enum):

    enum MonsterAttributes : IBitmaskRepresentable, IAutoBitmaskable {
        case Big, Ugly, Scary
    
        static var autoBitmaskValues : [MonsterAttributes] = [.Big, .Ugly, .Scary,]
        var  bitmaskValue: UInt16  { return AutoBitmask..autoBitmaskValueFor(self) }
        init(bitmaskValue: UInt16) { self = AutoBitmask.autoValueFromBitmask(bitmaskValue) }
    }
    
    // various ways to initialize
    let option : MonsterAttributes = .Ugly
    
    let bitmaskOfOption        = Bitmask(option)
    let anotherBitmaskOfOption = |MonsterAttributes.Ugly // same as bitmaskOfOption
    
    let orWithVar = option | .Big                 // == Bitmask<MonsterAttributes> with a bitmaskValue of 1 | 2
    let simpleOr  = MonsterAttributes.Big | .Ugly // == Bitmask<MonsterAttributes> with a bitmaskValue of 1 | 2
    
    // getting the raw integral bitmask value
    let simpleOrValue = simpleOr.bitmaskValue                        // == UInt16(1 | 2)
    let orValue       = (MonsterAttributes.Big | .Ugly).bitmaskValue // == UInt16(1 | 2)
    
    // implements BooleanType
    if simpleOr & .Ugly             { /* this code will execute */ }
    
    // supports pattern matching operator
    if simpleOr ~= .Ugly            { /* this code will execute */ }
    if simpleOr ~= (.Ugly | .Scary) { /* this code will execute */ }
    

    ... and all you have to do is implement a one-property protocol.

    I'm really curious if anyone has any feedback on or ideas for the code, so please leave an issue in the queue if you think of anything!

    0 讨论(0)
提交回复
热议问题