Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typecasting and where in for loop

Tags:

swift

swift2

I have the following scenario:

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

let objects: [A] = ...

How can I loop through the array and only execute logic for the objects that are of type B?

Right now, I'm doing something like this:

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

But I was wondering if I can use where to make this more expressive and elegant.

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
like image 904
rid Avatar asked Jun 24 '15 08:06

rid


2 Answers

There is also for case (almost the same case as in switch statements) so it would look like this:

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

Another nice expression is:

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

so you can use methods, properties and so on from both protocols at the same time

like image 155
Qbyte Avatar answered Sep 22 '22 07:09

Qbyte


as? subtype and its variants are a code smell. The other answers here will help you accomplish what you want, but I wanted to suggest that you move this logic from the for loop to the protocol (if it's possible).

For example, consider a Shape protocol:

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

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

Create three shape types that conform to it:

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

As you know, you can create an array of shapes:

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

This approach lets you loop through this array without knowing their type:

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

This has two benefits:

  • Reduces coupling (your method running the for loop doesn't need to know what a Pentagon is)
  • Increases cohesion (logic related to Pentagon is contained within that type's definition)

I don't know for sure that this will work for your specific use case, but I think it's a better pattern generally.

like image 30
Aaron Brager Avatar answered Sep 22 '22 07:09

Aaron Brager