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

Posted

技术标签:

【中文标题】将类型传递给通用 Swift 扩展,或者理想情况下推断它【英文标题】:Pass in a type to a generic Swift extension, or ideally infer it 【发布时间】:2016-05-15 15:25:45 【问题描述】:

说你有

 class Fancy:UIView

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

    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  $0 != self 
                .flatMap  $0 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  $0 != self 
                .flatMap  $0 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  $0 != self 
         .flatMap  $0 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) 之类的类型,理想情况下,甚至还可以推断它。

【问题讨论】:

类似问题:***.com/questions/37216240/…(如果您认为它不能回答您的问题,那没关系 - 但这是同一个潜在问题)。 【参考方案1】:

只需使用.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  $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))

正确输出Fancy视图!


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

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

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


或者,您可以使用更swifty 的方式,并使用默认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 
        )

这将使您能够通过

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

甚至

for f : Fancy in self.siblings()

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

【讨论】:

@JoeBlow 是的,您实际上可以。我只是不想更改不必要的代码;) @JoeBlow 您可以将 type 参数设为可选,默认值为 nil (_: T.Type? = nil)。这将使您可以使用或不使用类型来调用该方法。标准推断应该正常工作。 @luk2302 你不是在游乐场吧? Playgrounds 是出了名的不稳定 ;) 在一个完整的项目中它对我来说工作正常。 @originaluser2 我当然是 :D 并且我知道,无论何时发生仍然很有趣。 "Swift。它可以让你花费非常长的时间来完善非常短的代码。" :)【参考方案2】:

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

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

或者我的偏好使用选项:

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

示例用法:

class ExampleView: UIView 

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

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

    

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

【讨论】:

信息也非常丰富。我点击了这个答案的赏金以及 - 再次感谢大家!

以上是关于将类型传递给通用 Swift 扩展,或者理想情况下推断它的主要内容,如果未能解决你的问题,请参考以下文章

如何在不将道具传递给底层 DOM 元素的情况下扩展样式组件?

Swift学习:扩展(Extensions)

泛型约束-swift

在 Swift 中传递值类型作为引用

Swift:将类型从属性传递给泛型函数

将用户名传递给通用 DetailView 时出错 - django 1.4.3