Подтвердить что ты не робот

Преобразование в родовое число из массива в Swift

У меня есть протокол под названием Composite.

Этот протокол имеет массив composites: [Composite]

У меня также есть общий подкласс GenericSubclass<T>: Composite

При повторении по массиву лучшее, что я могу придумать, выглядит так:

for item in composites {
    if let item = item as? GenericSubclass<A> {
        let sc = SomeOtherClass<A>
    } else if let item = item as? GenericSubclass<B> {
        let sc = SomeOtherClass<B>
    } //and so on...
}

Есть ли способ удержать GenericSubclass без указания Generic? В моем случае использования мне совершенно не нужно знать о T. Мне просто нужно создать экземпляр другого класса с тем же родовым типом.

Любая помощь очень ценится.

4b9b3361

Ответ 1

Непонятно, что вы пытаетесь выполнить с помощью "общих" (предназначенных для каламбуров) имен классов, которые вы выбрали. Я не думаю, что есть способ непосредственно выполнить то, что вы хотите. То есть вы не можете просто оставить его как общий T, потому что компилятор должен каким-то образом определить, что T будет использоваться во время выполнения.

Однако один из способов решения проблемы - поднять API в протокол Composite:

protocol Composite {
    var composites: [Composite] { get set }
    func otherClass() -> OtherProtocol
}

protocol OtherProtocol { }

class GenericSubclass<T>: Composite {
    var composites: [Composite] = []

    func otherClass() -> OtherProtocol {
        return SomeOtherClass<T>()
    }
}

class SomeOtherClass<T>: OtherProtocol {}

Итак, теперь, когда вы реализуете свой цикл, вы можете положиться на то, что, поскольку каждый элемент является Composite, вы знаете, что он должен предоставить экземпляр OtherProtocol с помощью метода otherClass():

var c = GenericSubclass<Int>()
c.composites = [GenericSubclass<Double>(), GenericSubclass<Int>(), GenericSubclass<Character>()]

for item in c.composites {
    let sc = item.otherClass()
    print(sc)
}

В качестве альтернативы, если только GenericSubclass должен продавать OtherProtocol, вы можете сделать тип возврата Optional и определить расширение для всех других реализаций Composite:

protocol Composite {
    var composites: [Composite] { get set }
    func optionalClass() -> OtherProtocol?
}

extension Composite {
    func optionalClass() -> OtherProtocol? {
        return nil
    }
}

Ответ 2

Я немного экспериментировал с этим на игровой площадке, и я придумал этот

protocol Composite {
   var composites: [Composite] { get set }
}

class GenericSubclass<T>: Composite {
   var composites: [Composite] = []
}

let subclass = GenericSubclass<String>()

for item in subclass.composites {
   let className = String(describing: type(of: item))
   let aClassType = NSClassFromString(className) as! NSObject.Type
   let instance = aClassType.init() // we create a new object

   print(instance) //Output: GenericSubclass<String>
}

Надеюсь, это поможет кому-то.

Ответ 3

Я думаю, что это невозможно сделать в массиве.

Пока вы создаете несколько разных GenericSubclass<T>, а затем помещаете их в массив, вы потеряете <T> независимо от того, что composites есть [Composite] или [Any].

// this line won't compile
let array = [GenericSubclass<Int>(),GenericSubclass<Double>()]
//error: heterogenous collection literal could only be inferred to '[Any]'; add explicit type annotation if this is intentional

Вы хотите получить что-то вроде этого func ниже, param должен быть GenericSubclass<T> для компиляции успеха

func genericFunc<T>(param:GenericSubclass<T>) {
    let sc = SomeOtherClass<T>()
    print(sc)
}

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

class Subclass {
    var type : Any
    init(type : Any) {
        self.type = type
    }
}

class SomeOtherClass : CustomDebugStringConvertible{

    var type : Any
    init(type : Any) {
        self.type = type
    }
    var debugDescription: String{
        return String(describing: type.self)
    }
}

let array : [Subclass] = [Subclass(type : Int.self),Subclass(type : Double.self),Subclass(type : String.self)]

let scArray = array.flatMap {SomeOtherClass(type:$0.type.self)}
print(scArray) // prints [Int, Double, String]

Ответ 4

Вам нужно добавить один метод к протоколу, который создает новый элемент Тип, поддерживающий этот протокол. Итак, теперь вы можете использовать перечисления, структуры и классы без каких-либо знаний о создании объекта определенного типа.
Вы можете играть на детской площадке с помощью следующего кода:

import UIKit

//This is your protocol
protocol MyAwesomeProtocol {
//this methods leaves implementaion detailes
//to concrete type 
    func createNewObject()->MyAwesomeProtocol
}

//Just create empty string
extension String: MyAwesomeProtocol {
    func createNewObject() -> MyAwesomeProtocol {
        return String()
    }
}

//create Enum with default value
extension UIControlState: MyAwesomeProtocol {
    func createNewObject() -> MyAwesomeProtocol {
        return UIControlState.normal
    }
}

//create viewController of any type
extension UIViewController: MyAwesomeProtocol {
    func createNewObject() -> MyAwesomeProtocol {
        return type(of:self).init()
    }
}

//This is test function
//it creates array of newly created items and prints them out 
//in terminal
func doSomeCoolStuffWith(items:[MyAwesomeProtocol]){

    var newItems = [MyAwesomeProtocol]()

    for anItem in items {
        let newOne = anItem.createNewObject()
        newItems.append(newOne)
    }

    print("created new ones:\n\(newItems)\nfrom old ones:\n\(items)\n")
}

doSomeCoolStuffWith(items: [UIControlState.focused,UIControlState.disabled])

doSomeCoolStuffWith(items: [UISplitViewController(),UINavigationController(),UICollectionViewController()])

doSomeCoolStuffWith(items: ["I","love","swift"])

Это приведет к следующему результату:

created new ones:
[__C.UIControlState(rawValue: 0), __C.UIControlState(rawValue: 0)]
from old ones:
[__C.UIControlState(rawValue: 8), __C.UIControlState(rawValue: 2)]

created new ones:
[<UISplitViewController: 0x7fa8ee7092d0>, <UINavigationController: 0x7fa8f0044a00>, <UICollectionViewController: 0x7fa8ee705f30>]
from old ones:
[<UISplitViewController: 0x7fa8ee7011e0>, <UINavigationController: 0x7fa8f004e600>, <UICollectionViewController: 0x7fa8ee708fb0>]

created new ones:
["", "", ""]
from old ones:
["I", "love", "swift"]