Некоторые первоначальные предостережения
Прохождение типов в Swift - почти всегда плохой запах. Swift не относится к метатипам как к типам.
Более того, ваш addValue
никогда не может быть записан как указано. Вы не можете передать Контейнер в него или из него, потому что Контейнер - это общий протокол, который нельзя использовать в качестве типа (например, при указании параметра функции или типа возвращаемого значения функции).
Вы можете создавать универсальные классы, которые соответствуют универсальному протоколу, таким образом гарантируя, что вы можете push
к экземпляру любого такого класса. Но вы не можете в дальнейшем объединить их под какой-то одной головой, потому что они оба являются родовыми и могут быть решены по-разному.
Сказав все это, мы, вероятно, сможем довольно неплохо подойти к вашей идее, как я сейчас продемонстрирую.
Пересмотр вашего протокола и классов
Думая об общих вещах, которые вы пытаетесь сделать, я подозреваю, что вы ищете архитектуру, примерно такую:
protocol Pushable : class {
associatedtype T
init(_ t:T)
var contents : [T] {get set}
func push(_ t:T)
}
final class Stack<TT> : Pushable {
init(_ t:TT) { self.contents = [t]}
var contents = [TT]()
func push(_ t:TT) {
self.contents.append(t)
}
}
final class Queue<TT> : Pushable {
init(_ t:TT) { self.contents = [t]}
var contents = [TT]()
func push(_ t:TT) {
self.contents.insert(t, at:0)
}
}
Я назвал ваш Контейнер именем Pushable только потому, что способность вызывать push
- это все, что у нас сейчас общего. Вы заметите, что я добавил init
к протоколу Pushable; это так, что у нас есть способ разрешения универсального Pushable. Независимо от значения, с которым мы инициализируем Pushable, его тип становится его универсальным параметризованным типом; в данный момент этот экземпляр входит в contents
, и дальнейшие экземпляры могут быть отправлены, хотя позже я покажу, как это изменить.
Так что теперь мы можем сказать что-то вроде этого:
let stack = Stack("howdy")
stack.push("farewell")
let queue = Queue(1)
queue.push(2)
Радость, где пункты
Хорошо, теперь давайте вернемся к вашему желанию передать произвольное значение произвольному Pushable. Чтобы выразить это, используйте Pushable, не как переданный тип или тип возврата, а как ограничение для универсального типа. Это то, что нам разрешено использовать для общего протокола:
func push<TTT,P>(_ what:TTT, to pushable: P)
where P:Pushable, P.T == TTT {
pushable.push(what)
}
Заводской метод и передача метатипа
Но вы, несомненно, заметите, что я до сих пор не предоставил функцию с возможностью создания очереди или стека. Для этого нам действительно нужно было бы передать метатип. Ага, но я дал Pushable init
требование! Так что теперь мы можем сделать это:
func createStackOrQueue<TTT,P>(_ what:TTT, type pushableType: P.Type) -> P
where P:Pushable, P.T == TTT {
return P.init(what)
}
let stack = createStackOrQueue("howdy", type:Stack.self)
Это не то же самое, что вы пытались сделать, но, возможно, это достаточно близко, чтобы вы начали.
Заводской метод, передавая только метатипы
Если вы действительно настаиваете на передаче метатипов, давайте изменим init
, чтобы он тоже принимал метатип:
protocol Pushable : class {
associatedtype T
init(_ t:T.Type)
var contents : [T] {get set}
func push(_ t:T)
}
final class Stack<TT> : Pushable {
init(_ t:TT.Type) { self.contents = [TT]()}
var contents = [TT]()
func push(_ t:TT) {
self.contents.append(t)
}
}
final class Queue<TT> : Pushable {
init(_ t:TT.Type) { self.contents = [TT]()}
var contents = [TT]()
func push(_ t:TT) {
self.contents.insert(t, at:0)
}
}
Теперь мы можем написать универсальную фабричную функцию, очень близкую к той, что была у вас изначально, где и Pushable (стек или очередь) и тип контента выражаются в виде метатипов:
func createPushable<TTT,P>(_ whatElementType:TTT.Type, type pushableType: P.Type) -> P
where P:Pushable, P.T == TTT {
return P.init(TTT.self)
}
Не могу сказать, что одобряю подобные вещи, но, по крайней мере, вы можете видеть, как это делается.
Что-то очень похожее на вашу оригинальную идею
И теперь я думаю, что мы можем сделать что-то очень близкое к вашей первоначальной концепции, где мы говорим, хотим ли мы, чтобы стек или очередь вместе с чем-то давили на это! Готов?
func createPushable<TTT,P>(type pushableType: P.Type, andPush element:TTT) -> P
where P:Pushable, P.T == TTT {
let result = P.init(type(of:element).self)
result.push(element)
return result
}
А вот как это назвать:
let stack = createPushable(type:Stack.self, andPush:"howdy")