将类型传递给通用 Swift 扩展,或者理想地推断它

Pass in a type to a generic Swift extension, or ideally infer it

假设你有

 class Fancy:UIView

您想查找所有同级 Fancy 视图。 ...

    for v:UIView in superview!.subviews
        {
        if let f = v as? Fancy
            { f.hungry = false }
        }

所以,尝试扩展,

public extension UIView
    {
    internal func fancySiblings()->([Fancy])
        {
            return (self.superview!
                .subviews
                .filter { [=13=] != self }
                .flatMap { [=13=] as? Fancy }
                )
        }
    }

太棒了,你现在可以了

    for f:Fancy in self.fancySiblings()
        { f.hungry = false }

太棒了。

但是,

如何将该扩展概括为适用于任何 UIView 子类型?

理想情况下,扩展 甚至可以推断类型 ?以及打字?

所以,像...

public extension UIView
    {
    internal func siblings<T>( something T )->([T])
        {
            return (self.superview!
                .subviews
                .filter { [=15=] != self }
                .flatMap { [=15=] as? T }
                )
        }

然后你可以这样称呼它...

    for f in self.siblings(Fancy)
    for p in self.siblings(Prancy)
    for b in self.siblings(UIButton)

你怎么能像那样“告诉”通用扩展要使用的类型?

看来你可以“反推”了,

public extension UIView
    {
    internal func incredible<T>()->([T])
        {
        return (self.superview!
         .subviews
         .filter { [=17=] != self }
         .flatMap { [=17=] as? T }
         )
        }


    for f:Fancy in self.incredible()

    for p:Prancy in self.incredible()

这很神奇,但反过来就不行了。

你甚至可以...

    self.siblings().forEach{
        (f:Fancy) in
        d.hasRingOn = false
        }

所以我仍然想知道如何“传入”类似 for f in self.siblings(Fancy) 的类型,理想情况下,甚至还可以推断它。

只需使用 .Type:

internal func siblings<T>( something : T.Type)->([T]) {
    ...
}

之后 for f in self.siblings(Fancy) 应该完全按预期工作。

完整的工作示例:

class Fancy : UIView {}

public extension UIView {
    internal func siblings<T>( _ : T.Type)->([T]) {
        return (self.superview!
            .subviews
            .filter { [=11=] != self }
            .flatMap { [=11=] as? T }
        )
    }
}

let superView = UIView()
let view = UIView()
superView.addSubview(view)
superView.addSubview(UIView())
superView.addSubview(Fancy())

print(view.siblings(Fancy))

正确输出一个 Fancy 视图!


解决请求的添加,以选择性地使用显式类型参数或使编译器的类型推断生效。您可以在同一扩展中创建第二个方法

internal func siblings<T>()->([T]) {
    return siblings(T)
}

这种方式提供显式类型参数调用方法一,省略它需要您使其可推断并调用第二个函数,后者在内部调用第一个函数。


,您可以使用更 快速 的方式,并使显式类型参数成为可选的,默认为 nil .值得注意的是,这将在省略类型参数的情况下强制进行推断:

// power extension, it provides both infered or stated typing
internal func siblings<T>(_ : T.Type? = nil) -> ([T]) {
    return (self.superview!
        .subviews
        .filter { [=13=] != self }
        .flatMap { [=13=] as? T }
        )
}

这将使您能够通过

调用该方法
for f in self.siblings(Fancy)

甚至

for f : Fancy in self.siblings()

两者都可以工作,但仍然只定义一个函数。

与之前所说的类似的答案,但更加精简,无需传递任何内容或多次迭代子视图:

extension UIView {
    internal func siblings<T: UIView>() -> [T] {
        return superview?.subviews.flatMap {return ([=10=] == self) ? nil : ([=10=] as? T) } ?? []
    }
}

或者我对使用选项的偏好:

internal func siblings<T: UIView>() -> [T]? {
        return superview?.subviews.flatMap {return ([=11=] == self) ? nil : [=11=] as? T } 
}

用法示例:

class ExampleView: UIView {

    func getMatchingSiblings(){
        let foundSiblings: [ExampleView] = siblings()
    }

    //or with the for loop in the question:
    for item: ExampleView in siblings() {

    }
}

在处理泛型时,您只需要在方法的签名中包含一个泛型类型的实例。因此,如果您有参数或使用泛型的 return 类型,则无需传递类型。