1

给定一个泛型类型的结构,我想限制哪些方法只能调用泛型类型的子类型。

struct ViewBuilder<T:UIView> {

    let parent:UIView

    func createView() -> UIView {
        let view = UIView()
        parent.addSubview(view)
        return view
    }
}

extension ViewBuilder where T:UIButton {

    func createButton() -> UIButton {
        let button = UIButton()
        parent.addSubview(button)
        return button
    }
}

在这段代码中,我指定构建器应该只创建UIControls

let builder = ViewBuilder<UIControl>(parent:UIView())
let view = builder.createView() // ok
let button = builder.createButton() //  'UIControl' is not a subtype of 'UIButton'

但是,如您所见,我得到了相反的行为;aUIView不是a ;_ UIControl一个UIButton 一个UIControl。有什么方法可以在 Swift 中做到这一点?(我使用的是 Swift 4。)我使用泛型和协议尝试了几件事,但没有运气。

(我理解为什么上面的代码会以它的方式工作,但如果存在整体问题,我正在寻找替代方案。如果有人能提出一个好的解决方案,也许我可以改进这个问题以适应。)

4

1 回答 1

1

无法从子类型中删除方法(T where T:...是 的子类型T)。这是类型的基本特征。一个子类型必须能够做一个类型可以做的所有事情。否则,如果一个函数采用 generic ViewBuilder<T>,它如何知道是否createView允许使用它?扩展就是扩展。这种类型的消费者甚至可能看不到它们。

原则上,这是您真正要求的:

struct ViewBuilder<T: UIView> {

    let parent: UIView

    func create<U:T>() -> U {
        let view = U()
        parent.addSubview(view)
        return view
    }
}

create为任何属于 的子类型的类型提供了一种方法,该类型T本身必须是 的子类型UIView。不幸的是,目前这不是合法的 Swift ( SR-5213 )。问题在于,受类约束的泛型类型参数本身并不被视为用于约束其他类型参数的类。

鉴于此限制,在大多数情况下,我可能会使用这样的组合:

struct ViewBuilder {
    let parent: UIView

    func create<View: UIView>(_ type: View.Type) -> View {
        let view = View()
        parent.addSubview(view)
        return view
    }
}

struct ControlBuilder {
    private let builder: ViewBuilder
    var parent: UIView { return builder.parent }

    init(parent: UIView) {
        builder = BaseViewBuilder(parent: parent)
    }

    func create<Control: UIControl>(_ type: Control.Type) -> Control {
        return builder.create(type)
    }
}

let builder = ControlBuilder(parent:UIView())
let button = builder.create(UIButton.self)

这里是ControlBuilderHASAViewBuilder而不是 ISA ViewBuilder。如果您想接受“可以创建一种视图的东西”,这有一些限制,因为几乎不可能(据我所知)创建一个涵盖这两者的协议,因为 SR-5213。但是鉴于您的示例,这看起来与您的用例相匹配。

(不过,我对整个用例有点怀疑。我不清楚“只能包含控件的视图”有什么用处。感觉你真正想要的是扩展UIView。“Builder”感觉像是一种尝试将 Java 模式导入可能不适合的 Swift。)

于 2017-06-27T13:59:07.643 回答