Массив поиска для объекта предоставленного типа

У меня есть массив животных. Я хочу найти его для определенного типа подкласса. Массив Animals всегда будет содержать только по одному экземпляру каждого типа подкласса. Я пробовал следующее, что не работает. Я получаю сообщение об ошибке компиляции: «animalType не является типом».

public static func getAnimal<T: Animal>(animalType: T.type) -> Animal {
    for animal in self.animals {
        if animal is animalType {
            return animal
        }
    }
}

Возможно ли это в Swift?

Я хотел бы назвать это так...

AnimalServices.getAnimal(Dog)

person lionpants    schedule 02.03.2015    source источник
comment
Вы можете сделать if let cast как тип, и если он терпит неудачу, то животное не относится к этому типу.   -  person Ian    schedule 02.03.2015
comment
Я только что попробовал это. if let a = animal as animalType { } Компилятор по-прежнему жалуется, что animalType не является типом. Я нарушаю синтаксис?   -  person lionpants    schedule 02.03.2015


Ответы (4)


Вот упрощенный пример, обратите внимание, что я возвращаю необязательное животное, поскольку поиск может завершиться ошибкой (компилятор не знает, что у вас всегда будет только одно животное этого типа):

class Animal {
    let name: String

    init(name: String) {
        self.name = name
    }
}

class Cat: Animal {
    init() {
        super.init(name: "Cat")
    }
}

class Dog: Animal {
    init() {
        super.init(name: "Dog")
    }
}

struct Zoo {
    static var animals: [Animal] = []

    static func getAnimal<T: Animal>(animalType: T.Type) -> Animal? {
        for animal in animals {
            if animal is T {
                return animal
            }
        }
        return nil
    }
}

Zoo.animals.append(Cat())

Zoo.getAnimal(Cat) // Returns the cat as optional
Zoo.getAnimal(Dog)

Проще говоря, T — это ваш универсальный тип. T.Type укажите, что вы передаете тип T в качестве параметра.

person Matteo Piombo    schedule 02.03.2015
comment
Я был близок. ;) Это именно то, что я хотел. Спасибо! - person lionpants; 02.03.2015
comment
Или как хороший однострочник: return animals.filter { $0 is T }.first. Хотя и менее производительный. - person Rengers; 02.03.2015

Вы можете использовать оператор идентификации ===, чтобы проверить, равен ли тип элемента массива общему типу:

if animal.dynamicType === animalType {
    return animal
}
person Antonio    schedule 02.03.2015
comment
Нет, ты не можешь. Компилятор выдаст ошибку: бинарный оператор '===' не может применяться к операндам типа 'Animal.Type' и 'T' - person kellanburket; 02.03.2015
comment
@blacksquare, вы, должно быть, делаете что-то не так - это отлично работает в раскадровке, и, кроме того, я не использую T в этом фрагменте кода ... - person Antonio; 02.03.2015
comment
Я только что проверил ваш код в Swift 1.1, и он работает. Я тестировал с помощью swift 1.2, и он выдал ошибку, так что будьте осторожны в будущем. Я использую синтаксис, который я использую для сравнения с возвращаемыми строковыми значениями, что является допустимым сравнением, поскольку они оба являются строками. В любом случае лучший ответ на вопрос - просто проверить is T. - person kellanburket; 02.03.2015
comment
Ваше решение сработало, но я предпочел @Matteo Piombo. Спасибо! - person lionpants; 02.03.2015
comment
@lionpants: Да, я согласен, это правильный способ сделать это. - person Antonio; 02.03.2015
comment
@blacksquare Я тестировал версию 1.2 прямо сейчас, и, похоже, она работает — и, как указано выше, я согласен с вашим последним утверждением :) - person Antonio; 02.03.2015
comment
вы правы - прошу прощения. Как только я перезагрузил игровую площадку, она перестала выдавать эту ошибку. Первоначально у меня был ваш пример, использующий равенство ==, а затем переключил его на идентификатор === - должно быть, это была какая-то ошибка xcode. фу. что ты можешь сделать? - person kellanburket; 02.03.2015

Вы не можете использовать синтаксис is или as с неизвестным типом. Тип отражения типа, который вы ищете, может быть выполнен с использованием метода NSObject kindOfClass, если Animal является подклассом NSObject:

class Animal: NSObject {

    class func getAnimalOfType<T: Animal>(animals: [Animal], animalType: T.Type) -> Animal? {
        for animal in animals {
            if animal.isKindOfClass(animalType) {
                return animal
            }
        }
        return nil
    }

}

class Zebra: Animal { }
class Whale: Animal { }
var animals = [Zebra(), Whale()]
Animal.getAnimalOfType(animals, animalType: Zebra.self)

Если Animal не является подтипом NSObject, вы всегда можете сделать это, но мне это кажется немного хакерским:

 if "\(animal.dynamicType)" == "\(animalType)" {
      return animal
 }
person kellanburket    schedule 02.03.2015

struct Test<T: Hashable> {

    static func test(t: Any) {
        println(t is T)
    }
}

Test<Int>.test(3)

В этом примере я передаю тип (Int в моем случае, animalType в вашем) в структуру и переменную (мой Int(3), ваше животное) в качестве параметра теста статической функции. Я использовал T: Hashable, вы захотите протестировать T: Animal.

person MirekE    schedule 02.03.2015