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

时间:2023-01-02 22:00:44

Say you have

说你有

 class Fancy:UIView

you want to find all sibling Fancy views. 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?

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.

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

2 个解决方案

#1


18  

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.

之后对于self.siblings(Fancy)中的f应该完全按预期工作。

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!

正确输出一个花式视图!


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. That, remarkably, will force the inference in case of omitting the type argument:

或者,您可以使用更加流畅的方式,并使显式类型参数成为可选的默认值为nil。显然,这将在省略类型参数的情况下强制推断:

// 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.

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

#2


2  

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.

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

#1


18  

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.

之后对于self.siblings(Fancy)中的f应该完全按预期工作。

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!

正确输出一个花式视图!


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. That, remarkably, will force the inference in case of omitting the type argument:

或者,您可以使用更加流畅的方式,并使显式类型参数成为可选的默认值为nil。显然,这将在省略类型参数的情况下强制推断:

// 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.

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

#2


2  

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.

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