2017-01-06 4 views
2

У меня есть небольшая проблема. Мне нужно указать возвращаемое значение для функции, которая может возвращать каждую реализацию протокола. Для примера:Тип возврата для всех реализаций протокола

Мой Протокол:

protocol MyProtocol { 
    //some functions 
} 

Реализации:

class ClassA: MyProtocol { 
} 

class ClassB: MyProtocol { 
} 

Функция "проблема":

func getClassByString(_ name: String) -> MyProtocol.Type { 
    switch name { 
    case "a": 
     return ClassA.self 
    case "b": 
     return ClassB.self 
    default: 
     return ClassC.self 
    } 
} 

// EDIT: Это где я нужен результат

final class Mapper<N: MyProtocol> { 
    public func map() -> N?{ 
     if let klass = N.self as? MyProtocol.Type { 
      return klass as? N 
     } 
     return nil 
    } 
} 

Использование:

let className = "a" //this String comes from a JSON 
let result = getClassByString(className) 

let mappingResult = Mapper<result>().map() //undeclared identifier 'result' error 
let mappingResult = Mapper<ClassA>().map() //works but i do not know if it is ClassA 

Проблема в том, что result не очень ClassA.Type, что это должно быть, теперь MyProtocol.Type и я не могу передать это к следующей функции. Когда я даю result конкретное значение ClassA.self все работает. Я не могу бросить его as! ClassA.self, потому что я не знаю, если IST должен быть ClassA или ClassB или Class9000

Таким образом, вопрос. Есть ли другой тип возврата, например MyProtocol.Type, для функции getClassByString() или совершенно другим способом получить ClassA.Type до result?

ответ

1

Я думаю, что ваша проблема здесь не совсем так, как вы описали - result вашего примера на самом деле выглядит как ClassA.Type в Playground, но проблема, которую я подозреваю, это то, что вы делаете с ней в следующий раз. В вашем протоколе не сказано , как такие типы должны создаваться в общем виде, поэтому возвращаемый тип не может быть be экземпляр.

Я сделал несколько изменений в вашем примере, и теперь он работает ...

protocol MyProtocol { 
    //some functions 
    init() // To instantiate generically, there MUST be an accepted pattern for init() 
} 

class ClassA: MyProtocol { 
    required init() {} 
}  
class ClassB: MyProtocol { 
    required init() {} 
} 
class ClassC: MyProtocol { 
    required init() {} 
} 

func getClassByString(_ name: String) -> MyProtocol.Type { 
    switch name { 
    case "a": 
     return ClassA.self 
    case "b": 
     return ClassB.self 
    default: 
     return ClassC.self 
    } 
} 

let className = "a" //this String comes from a JSON 
let result = getClassByString(className) // ClassA.Type 
let a = result.init() // ClassA 
+0

Обратите внимание, что хотя 'типа (: а)' 'сообщит ClassA', мы не можем назвать, например, метод экземпляра 'ClassA', который не включен в' MyProtocol' на 'a'. Скажем, 'ClassA' имеет метод' foo() ->() ', то' a.foo() 'даст сообщение об ошибке _" error: значение типа 'MyProtocol' не имеет члена' foo'' _. Но я предполагаю, что это ожидалось, или статическая типизация Swift в этом случае потерпит неудачу (неопределенный во время компиляции). – dfri

+0

@dfri - Это точно моя точка - вызов 'init()' it A) должен быть указан в протоколе, а B) должен быть «обязательным» в реализации ... – Grimxn

+0

Действительно, я указываю это явно (в основном для OP), поскольку я считаю, что OP пытается решить проблему XY, используя методы, которые не подходят для статически типизированного языка как Swift. В конце концов, OP будет работать только с многоуровневым типом «MyProtocol», поскольку во время компиляции мы никогда не узнаем конкретный тип 'a', который заставил OP рассуждать над оценками как' (a as! ClassA) .foo() '. Я предполагаю, что продолжение в этом направлении приведет к тому, что код будет пахнуть рассуждениями для OP, и он/она может захотеть повторно посетить исходную проблему, прежде чем приступать к попытке «типизации во время выполнения». – dfri

 Смежные вопросы

  • Нет связанных вопросов^_^