简体   繁体   English

如何在Swift中将通用类型限制为另一个通用类型?

[英]How to constraint generic type to another generic type in Swift?

I'd like to do something like this: 我想做这样的事情:

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)
  } 
}

So later, for example, I can apply a Config to a UILabel: 因此,例如,稍后,我可以将Config应用于UILabel:

class RedViewConfig<T:UIView> : Config<T> {
  func configure(x:T) {
    x.backgroundColor = .redColor();
  } 
}

let label = UILabel() 
Config.apply(RedViewConfig(), to:label)

Or extend Config classes: 或扩展Config类:

class RedLabelConfig<T:UILabel> : RedViewConfig<T> {
  func configure(x:T) {
    super.configure(x)
    x.textColor = .redColor();
  } 
}

Config.apply(RedLabelConfig(), to:label)

I tried to do it, but I couldn't constraint classes. 我尝试这样做,但是我不能约束类。 So I tried with protocols and associated types, but when subclassing I found problems ( like this ) when overriding the associated type. 因此,我尝试使用协议和关联类型,但是在子类化时,在重写关联类型时发现了问题( 例如 )。

Do you actually need the generic parameter B ? 您实际上是否需要通用参数B If your argument to: was typed as A as well, it could be any subtype of A . 如果您的参数to:也输入为A ,则它可以是A任何子类型。 Like such: 像这样:

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())

Classes make this way too complicated. 类使这种方式过于复杂。 Inheritance is almost always a bad idea in Swift if you can possibly avoid it. 如果可以避免的话,在Swift中继承几乎总是一个坏主意。

Structs, though closer, still make this a bit over-complicated and restrictive. 结构虽然更接近,但仍然使它过于复杂和受限。

Really, these configurators are just functions. 实际上,这些配置器只是功能。 They take a thing and they do something to it, returning nothing. 他们拿东西,然后对它做点什么,什么也没返回。 They're just T -> Void . 它们只是T -> Void Let's build a few of those. 让我们构建其中一些。

func RedViewConfig(view: UIView) { view.backgroundColor = .redColor() }
func VisibleConfig(view: UIView) { view.hidden = false }

And we can use them pretty easily: 我们可以很容易地使用它们:

let label = UILabel()
VisibleConfig(label)

We can compose them (like super , but without the baggage) if their types are compatible: 如果它们的类型兼容,我们可以组成它们(像super ,但没有行李):

func RedLabelConfig(label: UILabel) {
    RedViewConfig(label)
    label.textColor = .redColor()
}

We can pass them around in data structures, and the compiler will apply the right covariance for us: 我们可以在数据结构中传递它们,编译器将为我们应用正确的协方差:

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) }

Now if we want other syntaxes, we can build those pretty easily too. 现在,如果我们需要其他语法,我们也可以轻松构建它们。 Something more like your original: 更像您的原始作品:

func applyConfig<T>(f: T -> Void, to: T) {
    f(to)
}
applyConfig(VisibleConfig, to: label)

or even closer to your original: 甚至更接近您的原始照片:

struct Config {
    static func apply<T>(config: T -> Void, to: T) { config(to) }
}

Config.apply(VisibleConfig, to: label)

The point is that just using functions here makes everything very flexible without adding any of the complexity of class inheritance or even structs. 关键是,仅在此处使用函数就可以使一切变得非常灵活,而无需增加类继承甚至结构的复杂性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM