Невозможно выполнить методы объектов, хранящихся в массиве [Any] - PullRequest
0 голосов
/ 18 сентября 2018

Я хочу хранить объекты разных типов в массиве.Программа ниже - только минимальная демонстрация.В anyArray: [Any] хранится экземпляр Object1.Оператор print выводит ожидаемый тип объекта.В следующей строке проверка типа хранимого объекта возвращает true.Это означает, что во время выполнения правильный тип объекта известен, и все кажется нормальным.

    class Object1 {
        var name = "Object1"
    }

    var anyArray:[Any] = [Object1()]
    print("\(type(of: anyArray[0]))")
    let testResult = anyArray[0] is Object1
    print("Test result:\(testResult)")
    //print("Name:\((anyArray[0]).name)")

Console output:
   Object1
   Test result:true

Однако, если я пытаюсь распечатать свойство name объекта, я получаю сообщение об ошибке от редактора:

Value of type 'Any' has no member 'name'

Ну, во время компиляции тип объектанеизвестноВот почему компилятор жалуется.Как я могу сказать компилятору, что все в порядке для доступа к свойствам хранимого объекта?

Ответы [ 2 ]

0 голосов
/ 18 сентября 2018

Разница происходит от разницы от Проверка типа in:

  • время выполнения или
  • время компиляции

Оператор is во время выполнения проверяет, можно ли привести выражение к указанному типу.type(of:) проверяет во время выполнения точный тип без учета подклассов.

anyArray[0].name не компилируется, поскольку тип Any не имеет свойства name.

Если вы уверены, что anyArray[0] - это Object1, вы можете использовать оператор downcast as!:

print("\((anyArray[0] as! Object1).name)")

Чтобы проверить во время выполнения, может ли элемент из anyArray быть Object1 используйте необязательную привязку, используя оператор условного приведения as?:

  • , если разрешено:

    if let object = anyArray[0] as? Object1 {
        print(object.name)
    }
    
  • Или используйте guard оператор, если вы хотите использовать этот объект в остальной области:

    guard let object = anyArray[0] as? Object1 else {
        fatalError("The first element is not an Object1")
    }
    print(object.name)
    

Если все объекты в вашем массиве имеют свойство name, и выне хочу многократно проходить через все циклы необязательного связывания, а затем использовать протокол.Ваш код будет выглядеть следующим образом:

protocol Named {
    var name: String {get set}
}

class Object1: Named {
    var name = "Object1"
}

var anyArray:[Named] = [Object1()]
print("\(type(of: anyArray[0]))")
let testResult = anyArray[0] is Object1
print("Test result:\(testResult)")
print("Name:\(anyArray[0].name)")

Обратите внимание, что anyArray теперь является массивом Named объектов и что Object1 соответствует протоколу Named.

Чтобы узнать больше о протоколах, посмотрите здесь .

0 голосов
/ 18 сентября 2018

Ваш объект по-прежнему имеет тип Any. Вы только что проверили, может ли он иметь тип Object1, но не произвели его. Если вы хотите, чтобы объект был Object1, вам нужно привести it.

Также, если у нескольких классов может быть имя, вам нужно использовать Protocol, как @vadian упомянул в своем комментарии, и привести его к этому протоколу.

protocol NameProtocol {
    var name: String {get set}
}

class Object1: NameProtocol {
    var name = "Object1"
}

if let testResult = anyArray[0] as? NameProtocol {
     print(testResult.name)
}

Редактировать: «Я хочу хранить объекты разных типов в массиве». Решение, которое вы пометили как правильное, не будет работать, если все объекты, которые у вас есть, делают не соответствует протоколу.

...