【问题标题】:How to implement a method of a generic Swift 2.2 protocol returning that generic protocol如何实现返回该通用协议的通用 Swift 2.2 协议的方法
【发布时间】:2016-04-18 10:39:54
【问题描述】:

不太清楚如何为那个起一个好的标题...

我想定义一个通用协议GBucket,它有点像一组相同类型的项目。有点像CollectionType,但功能要少得多。像这样:

public protocol GBucket {

  associatedtype BElement

  func splitBucket
    <A: GBucket, B: GBucket where A.BElement == Self.BElement,
                                  B.BElement == Self.BElement>
    (idx: Int) -> ( A, B )
}

它本质上只是提供了一种将GBucket 拆分为两个新GBuckets 的方法。可以是符合协议的任何类型 - 即返回的部分不必是进行拆分的同一类。

我尝试将其作为示例实现:

 extension ArraySlice : GBucket {

  public typealias BElement = Generator.Element

  public func splitBucket
    <A: GBucket, B: GBucket where A.BElement == BElement,
                                  B.BElement == BElement>
    (idx: Int) -> ( A, B )
  {
    let ls : ArraySlice<A.BElement> = self[0..<idx]
    let a  : A = ls // this conversion FAILs
    return ( a, self[idx..<self.count] ) // this too, of course ;-)
  }
}

这会产生:

无法将'ArraySlice '类型的值转换为指定类型'A'

据我所知应该可以很好地转换。 ArraySliceGBucket 并且元素类型是相同的,这要归功于 where 规范。

另一个更短的示例说明了不使用数组的问题:

public protocol GBucket {
  associatedtype BElement
  func otherBucket<A: GBucket where A.BElement == Self.BElement>() -> A
}

public class MyBucketType<T> : GBucket {
  public typealias BElement = T

  public func otherBucket<A: GBucket where A.BElement == BElement>() ->     A {
    return MyBucketType<A.BElement>()
  }
}

我做错了什么?

【问题讨论】:

    标签: swift generics


    【解决方案1】:

    Rasmus 在评论中发布了一个正确的链接,很好地解释了问题中的方法失败的原因:Swift Types - Returning constrained generics from functions and methods。他没有修改他的答案,因此我提供了一个 - 这真的是他的;-)

    关键是在调用站点解析泛型。通用部分实际上更像是一个 C 宏。从概念上讲,它没有任何动态 - 它实际上是“用实际类型替换某些泛型类型”。

    看看这个:

    extension ArraySlice : GBucket {
    
      public typealias BElement = Generator.Element
    
      public func splitBucket
        <A: GBucket, B: GBucket where A.BElement == BElement,
                                      B.BElement == BElement>
        (idx: Int) -> ( A, B )
      {
        let ls : ArraySlice<A.BElement> = self[0..<idx]
        let a  : A = ls // this conversion FAILs
        return ( a, self[idx..<self.count] ) // this too, of course ;-)
      }
    }
    

    只有在调用splitBucket 方法(或通过其他方式专门化)时,类型 A 和 B 才会被替换为类型!示例:

    let aSlice : ArraySlice<Int> = myArray[1..<5]
    let result : ( Array<Int>, Array<Int> ) = aSlice.splitBucket(3)
    

    splitBucket 中的 AB 类型仅在此时扩展为 Array&lt;Int&gt;。 IE。该函数的专用(泛型扩展)版本将是:

    public func splitBucket(idx: Int) -> ( Array<Int>, Array<Int> ) {
      let ls : ArraySlice<A.BElement> = self[0..<idx]
      let a  : Array<Int> = ls // this conversion FAILs
      return ( a, self[idx..<self.count] ) // this too, of course ;-)
    }
    

    这就是let a 必须失败的原因。

    P.S.:这并没有回答如何实现目标,但它是一个起点:-)

    【讨论】:

      【解决方案2】:

      ArraySlice 符合 GBucket 和 A 符合 GBucket 并不意味着您可以在它们之间进行转换。您可以从更专业的类型转换为更通用的类型,但不能在两种不同的更专业的类型之间转换。

      也许下面的代码可以解决你的问题?

      public protocol GBucket : CollectionType {
          func splitBucket(idx: Int) -> ( Self, Self )
      }
      
      extension ArraySlice : GBucket {
          public func splitBucket(idx: Int) -> ( ArraySlice, ArraySlice )
          {
              let A = self[0..<idx]
              let B = self[idx..<self.count]      
              return (A, B)
          }
      }
      

      【讨论】:

      • “你可以从更专业的类型转换为更通用的类型”——据我所知,我没有。一个 is 通过 where 子句专门用于 BElement 到完全相同的类型 - 这正是我的意图。
      • 您的回答缺乏我的要求“返回的零件不必是进行拆分的同一类”。 IE。这应该是有效的:'extension _ArrayType : GBucket { func splitBucket() -> (ArraySlice, ArraySlice)'。也就是说,split 函数可以返回任何类型的GBucket,只要它与元素类型匹配即可。使用您的方法,_ArrayType 只能返回 _ArrayType,而不是 ArraySlices。
      • 行: let a : A = ls 失败,因为 A 可能是符合 GBucket 协议的 any 类。 ArraySlice 如何实现 A 的方法和属性?
      • 好的,试试看这个:stackoverflow.com/questions/27404137/…
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-10
      • 1970-01-01
      相关资源
      最近更新 更多