类型转换和 for 循环中的位置

Typecasting and where in for loop

我有以下场景:

protocol A {}
protocol B: A {}
protocol C: A {}

let objects: [A] = ...

如何遍历数组并只对 B 类型的对象执行逻辑?

现在,我正在做这样的事情:

for object in objects {
    if let b = object as? B {
        ...
    }
}

但我想知道我是否可以使用 where 使其更具表现力和优雅。

for b in objects where b is B // <- compiles, but b is typed as A, not B
for b: B in objects where b is B // <- doesn't compile
for b in objects as! [B] where b is B // <- I get a warning that "is" will always be true

我不是 100% 确定这会回答您的情况 - 因为您已经有类似的东西 - 而且我不完全理解您不喜欢您的版本的地方。然而,这适用于 Swift 2:

for object in objectArray where object is protocolB {
    //print(object) // only objects conforming to protocolB
}

这是我的声明:

var objectArray: [AnyObject] = []
// contains a mix of objects of the following 3 classes


class class01: protocolA {
}
class class02: protocolA, protocolB {
    func test() -> String {
    // required to conform to protocolB
    return "hello"
    }
}
class class03: protocolA, protocolB, protocolC {
    func test() -> String {
    // required to conform to protocolB
    return "hello"
    }
}

protocol protocolA {
}
protocol protocolB: protocolA {
    func test() -> String
}
protocol protocolC: protocolA {
}

compiles, but b is typed as A, not B

这就是我不明白的地方。很可能是因为我很笨。但是按照我的阅读方式,您的 protocolB 对象也符合定义的 protocolA。我的定义也是一样的。

还有 for case(与 switch 语句中的 case 几乎相同)所以它看起来像这样:

for case let b as B in objects {
  // use b which is now of type B
}

另一个很好的表达方式是:

for case let b as protocol<B, C> in objects {
  // use b which is now of type protocol<B, C>
}

因此您可以同时使用两种协议的方法、属性等

as? subtype 及其变体是一种代码味道。这里的其他答案将帮助您完成您想要的,但我想建议您将此逻辑从 for 循环移动到协议(如果可能的话)。

例如,考虑一个 Shape 协议:

protocol Shape {
    func draw()
    func executeSomeSpecialOperation()
}

extension Shape {
    func executeSomeSpecialOperation() {
        // do nothing by default
    }
}

创建三个符合它的形状类型:

struct Circle : Shape {
    func draw() {
        // drawing code goes here
    }
}

struct Diamond : Shape {
    func draw() {
        // drawing code goes here
    }
}

struct Pentagon : Shape {
    func draw() {
        // drawing code goes here
    }

    func executeSomeSpecialOperation() {
        print("I'm a pentagon!")
    }
}

如您所知,您可以创建一个形状数组:

let shapes : [Shape] = [Circle(), Diamond(), Pentagon()]

这种方法可以让您在不知道其类型的情况下遍历此数组:

for shape in shapes {
    shape.draw()
    shape.executeSomeSpecialOperation()
}

这有两个好处:

  • 减少耦合(您的方法 运行 for 循环不需要知道 Pentagon 是什么)
  • 增加凝聚力(与 Pentagon 相关的逻辑包含在该类型的定义中)

我不确定这是否适用于您的特定用例,但我认为它通常是一种更好的模式。