简体   繁体   English

扩展使用的具有通用类型别名的协议

[英]Protocol with generic typealias used by an extension

I am running into an issue with the json parsing portion of an application I am writing. 我正在编写的应用程序的json解析部分遇到问题。 I wrote a quick example in a playground shown below to illustrate the issue. 我在下面显示的操场上写了一个简单的例子来说明这个问题。 I have a parser type protocol and a collection of parser objects that conform to it. 我有一个解析器类型协议和一个符合它的解析器对象集合。 Everything works flawlessly except for the final line. 除最后一行外,其他所有内容均正常运行。 The compiler is having an issue figuring what class a generic should be, and I am not sure whether it's a mistake I have made or a bug with the compiler itself. 编译器在确定泛型应该属于哪个类时遇到问题,我不确定这是我犯的错误还是编译器本身的错误。

The error is produced by the final line of the example and reads: 该错误由示例的最后一行产生,内容为:

在此处输入图片说明

For this example I made the extension on Dictionary in the final few lines, but in my actual application I am using RxSwift, and the extension is on Observable as I am trying to write: 对于此示例,我在最后几行中对Dictionary进行了扩展,但是在我的实际应用程序中,我使用RxSwift,并且在尝试编写时,该扩展在Observable上:

API.someNetworkRequest().parsed().subscribeNext() { thing in } 

This error has no relation to RxSwift though. 该错误与RxSwift无关。

Also to note, I have tried using 'as!' 另请注意,我尝试使用“ as!” and a few other approaches to try and get the compiler understanding what type I want the generic constraint to be. 以及其他一些方法来尝试使编译器了解我希望通用约束为哪种类型。 If I could just write parsed() and feed that the generic constraint directly it would work, but I can't as it is a function and produces the error 'Cannot explicitly specialize a generic function'. 如果我可以直接编写parsed()并直接馈入泛型约束,它将起作用,但由于它是一个函数而不会产生错误“无法显式专门化泛型函数”,因此无法实现。

  protocol ParserType {
      typealias ParsedObjectType

      init()

      func parse(object: AnyObject) throws -> ParsedObjectType
  }    

  struct Dog {
      var name: String?
  }    

  class DogParser: ParserType {
      required init() { }

      func parse(object: AnyObject) throws -> Dog {
          return Dog()
      }
  }    

  struct PaginationContext {
      var nextPageLink: String?
  }    

  class PaginatedParser<T where T: ParserType>: ParserType {
      required init() { }

      func parse(object: AnyObject) throws -> ([T.ParsedObjectType], PaginationContext?) {
          // some array from json object
          let jsonArray = ["test", "test", "test"]

          let childParser = T()
          let parsedDogs: [T.ParsedObjectType] = jsonArray.flatMap() {
              do {
                  return try childParser.parse($0 as! AnyObject)
              } catch {
                  return nil
              }
          }

          let pagination = PaginationContext()

          return (parsedDogs, pagination)
      }
  }    


  // This works perfectly
  let paginatedParser = PaginatedParser<DogParser>()
  let thing = [String: AnyObject]()
  let parsedData = try! paginatedParser.parse(thing as! AnyObject)    



  extension Dictionary {
      func parsed<T where T: ParserType>() -> ([T.ParsedObjectType], PaginationContext?) {
          let paginatedParser = PaginatedParser<T>()
          return try! paginatedParser.parse(self as! AnyObject)
      }
  }    

  // This won't. The compiler can't figure out that it needs to use DogParser.
  let thing2 = [String: AnyObject]()
  let result: ([DogParser.ParsedObjectType], PaginationContext?) = thing2.parsed()

The extension method parsed() cannot infer that the generic type T is the same type as the immutable that you assign the result of the call to. 扩展方法parsed()无法推断通用类型T与您将调用结果分配给它的不可变类型相同。

You can solve this by supplying the parsed(..) method with the actual parser type as a parameter. 您可以通过为parsed(..)方法提供实际的解析器类型作为参数来解决此问题。 You needn't actually make explicit use of this parameter (hence internal name omitted using _ ), and its sole purpose in this context is to infer the type of the generic T . 您实际上不必显式使用此参数(因此,使用_省略内部名称),并且在此上下文中其唯一目的是推断通用T的类型。

// ...

extension Dictionary where Key: StringLiteralConvertible, Value: AnyObject {
    func parsed<T: ParserType>(_: T.Type) -> ([T.ParsedObjectType], PaginationContext?) {
        let paginatedParser = PaginatedParser<T>()
        return try! paginatedParser.parse(self as! AnyObject)
    }
}

With this, you needn't explicitly supply the type of result as it can be inferred from the return of parsed(..) . 这样,您无需显式提供result的类型,因为可以从parsed(..)的返回中推断出result的类型。 Note that I've also added a limitation in the extension for keys that conform to StringLiteralConvertible ( String , among others, see language ref. ) and for AnyObject values. 请注意,我还在扩展名中添加了一个限制,以扩展符合StringLiteralConvertible键( String ,等等,请参见语言参考 )和AnyObject值。 You shouldn't make extensions more general than they need to be. 您不应使扩展变得比需要的更笼统。

Example usage: 用法示例:

let thing2 = [String: AnyObject]()
let result = thing2.parsed(DogParser)

print(result.dynamicType)
    /* (Array<Dog>, Optional<PaginationContext>), OK */

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

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