2015-06-24 16 views
9

Ho il seguente scenario:Typecasting e dove nel ciclo for

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

let objects: [A] = ... 

come posso ciclo attraverso l'array ed eseguire solo la logica per gli oggetti che sono di tipo B?

In questo momento, sto facendo qualcosa di simile:

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

ma mi chiedevo se posso usare where per rendere questo più espressivo ed elegante.

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 

risposta

8

C'è anche for case (quasi lo stesso case come in switch dichiarazioni) in modo che sarebbe simile a questa:

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

Un'altra bella espressione è:

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

in modo da poter usare metodi, proprietà e così via da entrambi i protocolli allo stesso tempo

0

Io non sono sicuro al 100% che questo risponde il vostro caso - perché avete qualcosa di simile già - e non mi tutto capire che cosa non ti piace della tua versione. Tuttavia questo funziona a Swift 2:

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

Qui sono le mie dichiarazioni:

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 { 
} 

compila, ma b è tipizzata come A, non B

Questo è il bit I don capisco Molto probabilmente perché sono stupido. Ma il modo in cui sto leggendo questo, gli oggetti del protocollo B sono conformi anche al protocollo A come definito. Ho definito la mia lo stesso.

+0

Se 'protocolloB' definirebbe una funzione' test() ', non si sarebbe in grado di chiamarlo su' object' nella propria iterazione senza typecasting 'object' a' protocolB', perché 'object' è di tipo 'AnyObject', non di tipo' protocolloB'. – rid

+0

Nel codice aggiornato, se provi a chiamare 'print (object.test())' nel ciclo che hai commentato, riceverai un errore ('AnyObject' non ha un membro chiamato 'test()'). – rid

+0

Funziona perfettamente con Xcode7. Compreso 'print (oggetto)'. Questo è stato solo commentato perché era un esempio – simons

2

as? subtype e la sua variante sono un odore di codice. Le altre risposte qui ti aiuteranno a realizzare ciò che desideri, ma volevo suggerire di spostare questa logica dal protocollo for al protocollo (se possibile).

Ad esempio, si consideri un Shape protocollo:

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

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

Creare tre tipi di forme che si conformano ad esso:

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

Come sapete, è possibile creare una vasta gamma di forme:

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

Questo approccio consente di scorrere questo array senza conoscerne il tipo:

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

Questo ha due vantaggi:

  • Riduce accoppiamento (il metodo di esecuzione del ciclo for non ha bisogno di sapere che cos'è un Pentagon è)
  • Aumenta la coesione (logica relativa al Pentagon è contenuta all'interno della definizione di quel tipo)

Non so per certo che questo funzionerà per il tuo caso d'uso specifico, ma io inchiostro è generalmente un modello migliore.

+0

Ciò presuppone che il metodo abbia senso per tutte le 'forme '. Cosa faresti ad esempio se volessi calcolare il raggio medio di tutti i cerchi nella matrice 'shapes'? – rid

+0

Non memorizzerei tipi eterogenei in un array se devo eseguire operazioni che si basano sull'omogeneità. Ad esempio, potresti avere una matrice 'circles' separata e rendere' shapes' una proprietà calcolata. –

+0

Quindi ogni volta che è necessario un elenco di tutte le forme, verrà creata un'istanza di una nuova matrice? – rid