我想做这样的事情:
class Config<T> {
func configure(x:T)
// constraint B to be subclass of A
class func apply<A,B:A>(c:Config<A>, to:B) {
c.configure(to)
}
}
所以稍后,例如,我可以将配置应用于UILabel:
class RedViewConfig<T:UIView> : Config<T> {
func configure(x:T) {
x.backgroundColor = .redColor();
}
}
let label = UILabel()
Config.apply(RedViewConfig(), to:label)
或者扩展Config类:
class RedLabelConfig<T:UILabel> : RedViewConfig<T> {
func configure(x:T) {
super.configure(x)
x.textColor = .redColor();
}
}
Config.apply(RedLabelConfig(), to:label)
我试图这样做,但我无法限制类。所以我尝试使用协议和相关类型,但在子类化时,我在覆盖关联类型时发现了问题(like this)。
答案 0 :(得分:1)
您确实需要通用参数B
吗?如果您的参数to:
也被输入为A
,则它可以是A
的任何子类型。像这样:
class View {}
class LabelView : View {}
class Config<T> {
func configure(x:T) { print ("Configured: \(x)") }
}
func applyConfig<A> (c:Config<A>, to:A) {
c.configure(to)
}
applyConfig(Config<View>(), to: LabelView())
答案 1 :(得分:1)
类使这太复杂了。如果你可以避免继承,那么继承在Swift中几乎总是一个坏主意。
虽然结构更接近,但仍然使这有点过于复杂和限制。
真的,这些配置器只是功能。他们接受了一件事,他们为此做了一些事情,什么也没有回来。他们只是T -> Void
。让我们构建其中的一些。
func RedViewConfig(view: UIView) { view.backgroundColor = .redColor() }
func VisibleConfig(view: UIView) { view.hidden = false }
我们可以很容易地使用它们:
let label = UILabel()
VisibleConfig(label)
如果他们的类型兼容,我们可以撰写它们(如super
,但没有行李):
func RedLabelConfig(label: UILabel) {
RedViewConfig(label)
label.textColor = .redColor()
}
我们可以在数据结构中传递它们,编译器将为我们应用正确的协方差:
let configs = [RedLabelConfig, VisibleConfig]
// [UILabel -> ()]
// This has correctly typed visibleConfig as taking `UILabel`,
// even though visibleConfig takes `UIView`
// And we can apply them
for config in configs { config(label) }
现在,如果我们想要其他语法,我们也可以很容易地构建它们。更像你原来的东西:
func applyConfig<T>(f: T -> Void, to: T) {
f(to)
}
applyConfig(VisibleConfig, to: label)
或者甚至更接近你原来的:
struct Config {
static func apply<T>(config: T -> Void, to: T) { config(to) }
}
Config.apply(VisibleConfig, to: label)
重点是,在这里使用函数可以使一切变得非常灵活,而不会增加类继承甚至结构的任何复杂性。