扩展使用的泛型类型的协议

时间:2016-01-29 19:53:06

标签: swift

我遇到了我正在编写的应用程序的json解析部分的问题。我在下面的一个操场上写了一个简单的例子来说明这个问题。我有一个解析器类型协议和符合它的解析器对象的集合。除了最后一行,一切都完美无瑕。编译器在确定泛型应该是什么类时遇到问题,我不确定这是我犯的错误还是编译器本身的错误。

错误由示例的最后一行产生,并显示为:

enter image description here

对于这个例子,我在最后几行中对Dictionary进行了扩展,但是在我的实际应用程序中,我正在使用RxSwift,并且在我试图编写时,扩展名在Observable上:

  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()

此错误与RxSwift无关。

另外需要注意的是,我尝试使用&#39; as!&#39;以及一些其他尝试让编译器了解我希望泛型约束的类型的方法。如果我可以直接编写parsed()并直接输入泛型约束,那么它就可以工作,但我不能因为它是一个函数并产生错误而不能明确地专门化泛型函数&#39;。 / p>

<Grid>
    <StackPanel Orientation="Horizontal" >
        <ListBox x:Name="listbox1" Width=" 250" ItemsSource="{Binding listoffiles}" >

        </ListBox>
        <Button x:Name="btntest" Width=" 50" Height=" 20" VerticalAlignment="Top" Margin=" 10" Content=" Test it"></Button>
        <TextBox x:Name="textbox1" Height=" 50" Width=" 100"  Margin="0,135" Text="{Binding text}"/>
    </StackPanel>
</Grid> </Window>

1 个答案:

答案 0 :(得分: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 */
相关问题