2016-01-29 134 views
0

我正在与我写一个应用程序的JSON解析部分的问题,使用的通用typealias。我在下面的游乐场中写了一个快速示例来说明这个问题。我有一个解析器类型的协议和符合它的解析器对象的集合。除了最后一行,一切都完美无瑕。编译器在确定泛型应该是什么类时遇到了问题,我不确定这是我犯的错误还是编译器本身的错误。协议以扩展

错误是由该示例的最后一行生产中记载:

enter image description here

在这个例子中我做的最后几行词典的扩展,但在我的实际应用中,我使用RxSwift,并且该扩展是可观察到的,因为我尝试写:

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

这个错误没有关系,虽然RxSwift。

另外要注意,我已经尝试使用“作为!”以及其他一些尝试让编译器了解我想要的泛型约束是什么类型的方法。如果我可以直接编写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() 

回答

1

扩展方法parsed()不能推断泛型类型T是同样的类型分配调用的结果不变。

您可以通过将parsed(..)方法与实际的分析器类型作为参数一起解决此问题。您无需实际明确使用此参数(因此内部名称使用_省略),其唯一目的是推断通用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) 
    } 
} 

有了这个,你不必明确提供的result类型,因为它可以从parsed(..)返回来推断。请注意,我还添加了限制的扩展符合StringLiteralConvertibleString,除其他外,见language ref.)和AnyObject值的键。你不应该使扩展比他们需要的更普遍。

用法示例:

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

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

这结束了完美的工作。我尝试将参数类型作为参数传递,但认为它对于未使用的内部参数名称看起来很严重,而且必须可以让它在没有参数的情况下工作。使用下划线是我没有想到的。谢谢! – pmick