2016-01-04 11 views
8

Say mam kolekcji obiektów dziedziczących od wspólnej nadrzędnej (jest to korzystne z protokołami w tym przypadku):Swift rodzajowych: typ zwracany w zależności od typu parametru

class ObjectSuperClass { 
    type: ObjectType 
} 
class ObjectClass1: ObjectSuperClass { 
    type = .Type1 
} 
class ObjectClass2: ObjectSuperClass { 
    type = .Type2 
} 

szukam stworzyć ogólny funkcja wyszukiwania tak:

func objectsOfType<T: ObjectSuperClass>(T.class, otherFilter: Any?) -> [T] 

które mogłyby być użyte do wyszukania danego podtypu, wracając do bardziej szczegółowych tablicę wyników:

let result = objectsOfType(ObjectClass2.class, otherFilter: nil) -> [ObjectClass2] 
(pseudo-swift) 

Czuję, że to jest coś, co generycy mogą pomóc, ale nie widzą, gdzie należy umieścić ograniczenia. Czy to możliwe?

Odpowiedz

11

zadziwiająco dobrze to działa ...

func filterType<T>(list: [AnyObject]) -> [T] 
{ 
    return list.filter{ $0 is T }.map{ $0 as! T } 
} 

... Nie można przypisać wynik do czegoś, co zostało wyraźnie wpisane, jak w poniższym przykładzie:

class ObjectSuperClass: CustomStringConvertible 
{ 
    let myType: String 
    init(aString: String) 
    { 
     myType = aString 
    } 
    var description: String { return myType } 
} 

class ObjectClass1: ObjectSuperClass 
{ 
    init() 
    { 
     super.init(aString: "<t 1>") 
    } 
} 

class ObjectClass2: ObjectSuperClass 
{ 
    init() 
    { 
     super.init(aString: "<t 2>") 
    } 
} 

let unfilteredList: [AnyObject] = [ ObjectClass1(), ObjectClass2(), ObjectSuperClass(aString: "<Who knows>")] 

let filteredList1: [ObjectClass1] = filterType(unfilteredList) 
print("\(filteredList1)") // <t 1> 

let filteredList2: [ObjectClass2] = filterType(unfilteredList) 
print("\(filteredList2)") // <t 2> 

let filteredList3: [ObjectSuperClass] = filterType(unfilteredList) 
print("\(filteredList3)") // [<t 1>, <t 2>, <Who knows>] 

T jest implikowana w każdym przypadku z żądanego typu zwrotu. Sama funkcja filtruje oryginalną tablicę na podstawie tego, czy elementy są wymaganego typu, a następnie wymusza odfiltrowane wyniki do poprawnego typu.


Jeśli chcesz „dodatkowy filtr” nie trzeba jawnie wpisać wyniki tak długo, jak można wywnioskować T z dodatkową funkcję filtra.

func extraFilterType<T>(list: [AnyObject], extraFilter: T -> Bool) -> [T] 
{ 
    return list.filter{ $0 is T }.map{ $0 as! T }.filter(extraFilter) 
} 

let filteredList = extraFilterType(unfilteredList){ 
    (element : ObjectClass2) -> Bool in 
    !element.description.isEmpty 
} 

print("\(filteredList)") // <t 2> 
+0

To jest ładne, + 1.Out ciekawości, można skorzystać z 'własności .dynamicType' nawet sortowania tylko obiekty superklasy? Np., 'FiltersList3' w twoim przykładzie powyżej, aby uzyskać tylko' [] '? (Np. 'Return list.filter {$ 0.dynamicType == T.self} .map {$ 0 as!T} 'nie działa, ale coś wzdłuż tych linii?) Prawdopodobnie – dfri

+0

@dfri. Nie sprawdziłem. Nie jest to coś, o co normalnie bym się martwił, ponieważ jeśli nie potrafię traktować obiektów podklasy tak, jakby były obiektami klasy bazowej, biorę to jako zapach kodu wskazujący, że mój projekt klasy jest niewłaściwy. – JeremyP

+0

@dfri Właściwie to działa. Musisz tylko zadeklarować parametr dla pierwszego zamknięcia filtra jawnie jako 'AnyObject', tj.' Return list.filter {(anObj: AnyObject) -> Bool in anOj.dynamicType == T.self} .map {$ 0 as! T} ' – JeremyP

1

To najlepsze przybliżenie mogę wymyślić:

func objectsOfType<T: ObjectSuperClass>(type type: T.Type) -> [T] { 
    // Just returns an array of all objects of given type 
} 

func objectsOfType<T: ObjectSuperClass>(type type: T.Type, predicate: T -> Bool) -> [T] { 
    // Uses predicate to filter out objects of given type 
} 

Zastosowanie:

let bar = objectsOfType(type: ObjectClass1.self) 

let baz = objectsOfType(type: ObjectClass2.self) { 
    // Something that returns Bool and uses $0 
} 

Technicznie rzecz biorąc, można również przejść bez type argumentu powyżej, ale potem będzie musiało mieć jawnie wpisane odbiorniki (bar i baz w powyższym przykładzie), aby Swift mógł prawidłowo wnioskować o typach i użyj odpowiedniej wersji funkcji ogólnej.

+0

Dziękuję, gdybym mógł przyjąć wiele odpowiedzi, zrobiłbym to. – Ben

0

można zaimplementować funkcję tak:

func objectsOfType<T: ObjectSuperClass>(objects: [ObjectSuperClass], subclass: T.Type, otherFilter: (T->Bool)?) -> [T] { 
    if let otherFilter = otherFilter { 
     return objects.filter{$0 is T && otherFilter($0 as! T)}.map{$0 as! T} 
    } else { 
     return objects.filter{$0 is T}.map{$0 as! T} 
    } 
} 

Przykład użycia:

objectsOfType(arrayOfObjects, subclass: ObjectClass1.self, otherFilter: nil) 

Należy pamiętać, że nie jestem fanem przymusowej casting, jednak w tym przypadku nie powinno powodować problemy .

Albo, bardziej gadatliwy wersja funkcji, z jednym mniej wymuszonym Obsada:

func objectsOfType<T: ObjectSuperClass>(objects: [ObjectSuperClass], subclass: T.Type, otherFilter: (T->Bool)?) -> [T] { 
    return objects.filter({object in 
     if let object = object as? T { 
      if let otherFilter = otherFilter { 
       return otherFilter(object) 
      } else { 
       return true 
      } 
     } else { 
      return false 
     } 
    }).map({object in 
     return object as! T 
    }) 
} 
Powiązane problemy