简体   繁体   English

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

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

Say you have 说你有

 class Fancy:UIView

you want to find all sibling Fancy views. 你想找到所有兄弟的Fancy景观。 No problem ... 没问题 ......

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

So, try an extension, 所以,尝试扩展,

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

Awesome, you can now 太棒了,你现在可以

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

Fantastic. 太棒了。

But, 但,

How to generalize that extension to work with any UIView subtype? 如何推广该扩展以使用任何UIView子类型?

Ideally, can the extension infer the type , even? 理想情况下,扩展可以推断出类型吗? As well as taking a type? 还有一个类型?

So, something like ... 所以,像......

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

and then you could call it something like this ... 然后你可以称之为......

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

How can you "tell" a generic extension the type to use, like that?? 你怎么能“告诉”一个通用扩展使用的类型,比如??

It seems you can "infer it backwards", 看来你可以“向后推断”,

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


    for f:Fancy in self.incredible()

    for p:Prancy in self.incredible()

Which is amazing but doesn't work the other way. 这是惊人的,但不是相反的方式。

You can even... 你甚至可以......

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

So I would still like to know how to "pass in" a type something like for f in self.siblings(Fancy) and, ideally, even infer it also. 因此,我仍然想知道如何for f in self.siblings(Fancy) “传递”类似于for f in self.siblings(Fancy)的类型,理想情况下,甚至可以推断它。

Simply use the .Type : 只需使用.Type

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

Afterwards for f in self.siblings(Fancy) should work exactly as expected. 之后for f in self.siblings(Fancy)应该完全按预期工作。

Full working example: 完整的工作示例:

class Fancy : UIView {}

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

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

print(view.siblings(Fancy))

Correctly outputs the one Fancy view! 正确输出一个Fancy视图!


To address the requested addition for optionally using the explicit type parameter or take effect of the type inference of the compiler. 要解决所请求的添加,以便可选地使用显式类型参数或生成编译器的类型推断。 You can create a second method in the same extension 您可以在同一扩展中创建第二个方法

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

That way providing a explicit type parameter calls method one, omitting it will require you to make it inferable and will call the second function which in terms calls the first one internally. 这样提供一个显式类型参数调用方法一,省略它将要求你使它成为可推断的并将调用第二个函数,在内部调用第一个函数。


Or , you can use the far more swifty way and make the explicit type argument an optional with default nil . 或者 ,您可以使用更加流畅的方式,并使显式类型参数成为可选的默认值为 nil That, remarkably, will force the inference in case of omitting the type argument: 显然,这将在省略类型参数的情况下强制推断:

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

That will enable you to call the method either via 这将使您能够通过调用方法

for f in self.siblings(Fancy)

or even 甚至

for f : Fancy in self.siblings()

Both will work while still only defining one function. 两者都可以工作,但仍然只定义一个功能。

Similar answer to what's been said previously, but a little more streamlined and without having to pass anything or iterate over the subviews more than once: 对之前所说的内容进行了类似的回答,但更加精简,无需传递任何内容或不止一次遍历子视图:

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

or my preference using optionals: 或者我喜欢使用选项:

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

Example Usage: 用法示例:

class ExampleView: UIView {

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

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

    }
}

When dealing with generics, you simply need one instance of the generic type in the signature of the method. 在处理泛型时,您只需要在方法的签名中使用泛型类型的一个实例。 So if you have either a parameter, or return type that uses the generic, you don't need to pass the type. 因此,如果您有参数或使用泛型的返回类型,则无需传递类型。

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

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