Есть ли способ установить тип данных протокола в указателе функции?

#swift #types #protocols

#swift #типы #протоколы

Вопрос:

Есть ли способ установить тип данных протокола в указателе функции в swift?

Вот мой протокол ICRUDOperation

 public protocol ICRUDOperation {
    associatedtype T
    func insert(data:T)
    func update(data:T)
    func get(data:T) -> [T]
    func getList(data: BaseModel) -> [T]
    func getPage(data: BaseModel) -> [T]
    func delete(data: T)
}
  

Который я пытаюсь использовать в:

 func delegate1<W>(sqlite: W, service: W, data: W.T) where W: ICRUDOperation {
    sqlite.insert(data: data)
}
var decision = [String : [String:((ICRUDOperation, ICRUDOperation, T) ->())?]]()

func fillDecision() {
    decision["Person"]?["1"] = Delegate1
}
  

Я получаю эту ошибку при принятии решения

 Protocol 'ICRUDOperation' can only be used as a generic constraint because it has Self or associated type requirements
  

Ошибка для fillDecision() :

 Cannot assign value of type '(_, _, _.T) -> ()' to type '((ICRUDOperation, ICRUDOperation, _) -> ())??'
  

Комментарии:

1. Я изменил ваши заглавные буквы в именах функций и именах большинства переменных на строчные, что является стандартом Swift, чтобы улучшить читаемость

Ответ №1:

Как только вы добавили связанный тип, больше не существует такого понятия, как «ICRUDOperation». PAT (протокол со связанным типом) не имеет существующей формы; он существует для того, чтобы присоединять методы к другим типам или ограничивать, какие конкретные типы могут быть переданы универсальной функции. Вы не можете сохранить PAT в переменной или словаре или где-либо еще. Протокол (и вдвойне PAT) не является абстрактным классом.

Самое важное, что нужно понять, это то, что связанные типы выбираются реализацией, а не вызывающей стороной. Итак, в вашем примере T он будет выбран реализацией ICRUDOperation (точно так же, как массив выбирает свою коллекцию.Индекс должен быть Int; вы не можете выбрать это). Общие термины позволяют вызывающей стороне выбирать тип, который больше похож на то, чего вы пытаетесь достичь.

Как вы решаете эту проблему, зависит от вашего варианта использования, который трудно понять из вашего примера. Какова цель decision ?

Было бы полезно, если бы вы продемонстрировали, как, по вашему мнению, будут выглядеть две или три разные реализации ICRUDOperation. Я не уверен, что вы подразумеваете под «реализацией операции».

Ответ №2:

Я пытаюсь понять, как вы хотите это использовать, так что это моя идея о том, как использовать протокол. Я не уверен, отвечает ли это на ваш вопрос, но, возможно, это поможет вам приблизиться к решению.
Допустим, у нас есть эта модель, которую мы хотим сохранить

 struct Item {
    var id: Int
    var name: String
}
  

Тогда нам нужен обработчик, который может выполнять операции с БД

 struct ItemDbHandler: ICRUDOperation {
    typealias T = Item

    func insert(data: Item) {
        print("(#function) (item)")
    }

    func update(data: Item) {
        print("(#function) (item)")
    }

    func get(data: Item) -> [Item] { //shouldn't this return 1 element
        print("(#function) (item)")
        return []
    }
    // and so on...
}
  

И с некоторой функцией делегирования

 func delegateUpdate<W>(sqlite: W, service: W, data: W.T) where W: ICRUDOperation {
    sqlite.update(data: data)
}
  

мы можем работать с обработчиком напрямую или через функцию

 var item = Item(id: 1, name: "ABC")
var handler = ItemDbHandler()
handler.insert(data: item)
item.name = "abc"

delegateUpdate(sqlite: handler, service: handler, data: item)
  

Запуск этого на игровой площадке дает

вставить (данные:) Элемент (идентификатор: 1, имя: «ABC»)
обновление (данные:) Элемент (идентификатор: 1, название: «abc»)

Я не понимаю, что вы хотите сделать fillDecision , поэтому я пока пропустил это.