У меня есть код, который следует за общий дизайн:Wrong специализированная Обобщенная функция вызывается в Swift 3 от косвенного вызова
protocol DispatchType {}
class DispatchType1: DispatchType {}
class DispatchType2: DispatchType {}
func doBar<D:DispatchType>(value:D) {
print("general function called")
}
func doBar(value:DispatchType1) {
print("DispatchType1 called")
}
func doBar(value:DispatchType2) {
print("DispatchType2 called")
}
, где в действительности DispatchType
на самом деле является хранение бэкенд. Функции doBar
- это оптимизированные методы, которые зависят от правильного типа хранилища. Все работает отлично, если я:
let d1 = DispatchType1()
let d2 = DispatchType2()
doBar(value: d1) // "DispatchType1 called"
doBar(value: d2) // "DispatchType2 called"
Однако, если я функцию, которая вызывает doBar
:
func test<D:DispatchType>(value:D) {
doBar(value: value)
}
и я стараюсь подобный образец вызова, я получаю:
test(value: d1) // "general function called"
test(value: d2) // "general function called"
Это похоже на то, с чем Swift должен справиться, поскольку он должен во время компиляции определять ограничения типа. Так же, как быстрый тест, я также пытался писать doBar
как:
func doBar<D:DispatchType>(value:D) where D:DispatchType1 {
print("DispatchType1 called")
}
func doBar<D:DispatchType>(value:D) where D:DispatchType2 {
print("DispatchType2 called")
}
но получить те же результаты.
Любые идеи, если это правильно Быстрое поведение, и если да, то хороший способ обойти это поведение?
Редактировать 1: Пример того, почему я пытался избежать использования протоколов. Предположим, у меня есть код (значительно упрощено от моего фактического кода):
protocol Storage {
// ...
}
class Tensor<S:Storage> {
// ...
}
Для Tensor
класса У меня есть базовый набор операций, которые могут быть выполнены на Tensor
с. Однако сами операции будут изменять свое поведение на основе хранилища. В настоящее время я сделать это с:
func dot<S:Storage>(_ lhs:Tensor<S>, _ rhs:Tensor<S>) -> Tensor<S> { ... }
В то время как я могу положить их в Tensor
класса и использование расширений:
extension Tensor where S:CBlasStorage {
func dot(_ tensor:Tensor<S>) -> Tensor<S> {
// ...
}
}
это имеет несколько побочных эффектов, которые я не люблю:
Я думаю,
dot(lhs, rhs)
предпочтительнееlhs.dot(rhs)
. Функции удобства можно написать, чтобы обойти это, но это создаст огромный взрыв кода.Это приведет к тому, что класс
Tensor
станет монолитным. Я действительно предпочитаю, чтобы он содержал минимальный объем необходимого кода и расширял его функциональность вспомогательными функциями.Относится к (2), это означает, что любой, кто хочет добавить новую функциональность, должен будет коснуться базового класса, который я считаю плохим дизайном.
Edit 2: Один из вариантов является то, что все работает как ожидается, если вы используете ограничения на все:
func test<D:DispatchType>(value:D) where D:DispatchType1 {
doBar(value: value)
}
func test<D:DispatchType>(value:D) where D:DispatchType2 {
doBar(value: value)
}
заставит правильно doBar
называться. Это также не идеально, так как это вызовет много дополнительного кода для написания, но, по крайней мере, позволяет мне сохранить мой текущий дизайн.
Редактировать 3: Я столкнулся с документацией, показывающей использование ключевого слова static
с дженериками. Это помогает по крайней мере, с точки (1):
class Tensor<S:Storage> {
// ...
static func cos(_ tensor:Tensor<S>) -> Tensor<S> {
// ...
}
}
позволяет писать:
let result = Tensor.cos(value)
и поддерживает перегрузку операторов:
let result = value1 + value2
это иметь дополнительное многословие требуется Tensor
. Это может сделал немного лучше:
typealias T<S:Storage> = Tensor<S>
Это происходит из-за того, что Swift реализует методы отправки. Пожалуйста, взгляните на https://www.raizlabs.com/dev/2016/12/swift-method-dispatch/ - особенно в разделе Reference Type Matters. – courteouselk
Связанный: [Расширение коллекции с рекурсивным свойством/методом, зависящим от типа элемента] (http://stackoverflow.com/q/41640321/2976878) – Hamish
В некотором роде: [Как назвать более конкретный метод перегрузки] (http://stackoverflow.com/questions/41531569/how-to-call-the-more-specific-method-of-overloading) – dfri