Сделать класс делегатом для нескольких специализаций одного и того же общего типа, для которого требуется делегат - PullRequest
0 голосов
/ 29 сентября 2019

Ответ Хэмиша на этот вопрос на самом деле помог мне понять, как выполнить то, что я пытался сделать, - создать протокол для общего типа коллекции, для которого требуется делегаткоторая предоставляет функцию, которая принимает этот тип в качестве аргумента.Это помогло прояснить ситуацию, когда учебник Бенедикта Терхехте здесь просто не был для меня идеальным.Итак, прежде всего, СПАСИБО!Я пытался заставить это работать в течение пары недель, и, наконец, я понял.

Приведенный ниже код работает, как и ожидалось.

Мой вопрос: как мне его получить? продолжить работать после раскомментирования комментированных блоков инструкций, которые пытаются создать и использовать другой прогноз другого типа?Ошибка, в которой Swift ожидает, что Int и Double будут одного типа, в основном имеет смысл.Я понимаю, почему это не работает, как закодировано.Но можно сделать так, чтобы SomeViewOrSomething одновременно удовлетворял требованиям делегатов intForecast и doubleForecast, и если да, то как бы это выглядело?

Спасибо.

Отредактировано: Видимые опечатки, которые не влиялиповедение кода.

import Foundation

protocol ForecastableDelegate {
    associatedtype Element

    func forecastDidChange( _ forecast : Forecast< Element > )
}

protocol Forecastable {
    associatedtype Element

    var delegate : ForecastDelegate< Element > { get set }
    var contents : [ Element ]                 { get     }

    init( delegate : ForecastDelegate< Element >, length : Int, generator : @escaping () -> Element )

    func manifest() -> Element
}

class ForecastDelegate< T > : ForecastableDelegate {
    typealias Element = T

    private let _forecastDidChange : ( Forecast< Element > ) -> Void

    init< Delegate : ForecastableDelegate >( _ base : Delegate ) where Delegate.Element == Element {
        _forecastDidChange = base.forecastDidChange
    }

    func forecastDidChange( _ forecast : Forecast< Element > ) {
        _forecastDidChange( forecast )
    }
}

class Forecast< T > : Forecastable {
    typealias Element  = T

    internal       var delegate  : ForecastDelegate< Element >
    private( set ) var contents  : [ Element ]
    private        var generator : () -> Element

    required init< Delegate : ForecastableDelegate >(
        delegate : Delegate, length : Int, generator : @escaping () -> Element
    ) where Delegate.Element == Element {
        self.delegate  = ForecastDelegate< Element >( delegate )
        self.generator = generator
        self.contents  = ( 0..<length ).map { _ in return generator() }
    }

    func manifest() -> Element {
        let element = contents.removeFirst()

        contents.append( generator() )
        delegate.forecastDidChange( self )

        return element
    }
}

class SomeViewOrSomething : ForecastableDelegate {
    var intForecast    : Forecast< Int    >!
    //var doubleForecast : Forecast< Double >!

    init() {
        intForecast = Forecast< Int >( delegate : self, length : 5 ) {
            return ( 0...9 ).randomElement()!
        }

        /* doubleForecast = Forecast< Double >( delegate : self, length : 5 ) {
            return [ 0.0, 0.5, 1.0 ].randomElement()!
        } */
    }

    func forecastDidChange( _ forecast : Forecast< Int > ) {
        print( forecast.contents       )
    }

    /* func forecastDidChange( _ forecast : Forecast< Double > ) {
        print( forecast.contents.count )
    } */
}

let x = SomeViewOrSomething()

print( x.intForecast.manifest()    )
print( x.intForecast.manifest()    )
print( x.intForecast.manifest()    )
print( x.intForecast.manifest()    )
print( x.intForecast.manifest()    )
print( x.intForecast.manifest()    )

/* print( x.doubleForecast.manifest() )
print( x.doubleForecast.manifest() )
print( x.doubleForecast.manifest() )
print( x.doubleForecast.manifest() )
print( x.doubleForecast.manifest() ) */

Редактировать: Kinda / sorta, отвечая на мой собственный вопрос

Размышляя над проблемой больше, у меня возникла мысль: «Что ж, если я введу другой протокол дляэлемент прогноза?Оказывается, это своего рода работает, и его реализация выявила тот факт, что название Forecastable, вероятно, лучше подходило для прогнозируемого элемента, чем для конструкции, прогнозирующей его.Это также упрощает протокол ForecastDelegate, за исключением того, может быть, он упрощает его?

Мне кажется, что в этой реализацииcastDidChange лучше заменить его закомментированными специализациями.Однако по какой-то причине они не работают как перегрузки, и ForecastDidChange будет делать то же самое для двух разных AnyForecast , вместо того, чтобы обрабатывать их по-разному.Кажется, я обменял один выпуск на другой.

import Foundation

protocol Forecastable {}

protocol ForecastDelegate {
    func forecastDidChange< Element : Forecastable >( _ forecast : AnyForecast< Element > )
}

class AnyForecast< Element : Forecastable > {
    internal var delegate        : ForecastDelegate
    private( set ) var contents  : [ Element ]
    private        var generator : () -> Element

    required init( delegate : ForecastDelegate, length : Int, generator : @escaping () -> Element ) {
        self.delegate  = delegate
        self.generator = generator
        self.contents  = ( 0..<length ).map { _ in return generator() }
    }

    func manifest() -> Element {
        let element = contents.removeFirst()

        contents.append( generator() )
        delegate.forecastDidChange( self )

        return element
    }
}

extension Int    : Forecastable {}
extension Double : Forecastable {}

class SomeViewOrSomething : ForecastDelegate {
    var intForecast    : AnyForecast< Int    >!
    var doubleForecast : AnyForecast< Double >!

    init() {
        intForecast = AnyForecast< Int >( delegate : self, length : 5 ) {
            return ( 0...9 ).randomElement()!
        }

        doubleForecast = AnyForecast< Double >( delegate : self, length: 5 ) {
            return [ 0.0, 0.5, 1.0 ].randomElement()!
        }
    }

    func forecastDidChange< Element >( _ forecast : AnyForecast< Element > ) where Element : Forecastable {
        // Just pretending to do something useful here...
        switch forecast {
            case is AnyForecast< Int >    : print( forecast.contents       )
            case is AnyForecast< Double > : print( forecast.contents.count )
            default                       : ()
        }
    }

    /* func forecastDidChange< Int > ( _ forecast : AnyForecast< Int    > ){
        print( forecast.contents       )
    }

    func forecastDidChange< Double >( _ forecast : AnyForecast< Double > ){
        print( forecast.contents.count )
    } */
}

let x = SomeViewOrSomething()

print( "An element from intForecast:    " + x.intForecast.manifest().description    )
print( "An element from doubleForecast: " + x.doubleForecast.manifest().description )
...