2016-12-23 14 views
8

Tôi có một loạt các cấu trúc khác nhau, tất cả thực hiện giao thức Equatable và đang cố gắng chuyển nó tới một hàm mong đợi một bộ sưu tập where T.Iterator.Element: Equatable. Tôi biết cách giải quyết vấn đề này bằng cách sử dụng các lớp và chỉ cần tạo một class Vehicle: Identifiable, Equatable và sau đó thực hiện CarTractor triển khai Vehicle. Tuy nhiên tôi muốn biết nếu điều này là có thể với việc sử dụng cấu trúc và giao thức?Thao tác trên một mảng các cấu trúc thực hiện Equatable

Dưới đây là một ví dụ contrived về những gì tôi đang cố gắng để làm

//: Playground - noun: a place where people can play 

protocol Identifiable { 
    var ID: String { get set } 
    init(ID: String) 
    init() 
} 

extension Identifiable { 
    init(ID: String) { 
     self.init() 
     self.ID = ID 
    } 
} 

typealias Vehicle = Identifiable & Equatable 

struct Car: Vehicle { 
    var ID: String 

    init() { 
     ID = "" 
    } 

    public static func ==(lhs: Car, rhs: Car) -> Bool { 
     return lhs.ID == rhs.ID 
    } 
} 

struct Tractor: Vehicle { 
    var ID: String 

    init() { 
     ID = "" 
    } 

    public static func ==(lhs: Tractor, rhs: Tractor) -> Bool { 
     return lhs.ID == rhs.ID 
    } 
} 

class Operator { 
    func operationOnCollectionOfEquatables<T: Collection>(array: T) where T.Iterator.Element: Equatable { 
    } 
} 

var array = [Vehicle]() //Protocol 'Equatable' can only be used as a generic constraint because Self or associated type requirements 

array.append(Car(ID:"VW")) 
array.append(Car(ID:"Porsche")) 
array.append(Tractor(ID:"John Deere")) 
array.append(Tractor(ID:"Steyr")) 

var op = Operator() 
op.operationOnCollectionOfEquatables(array: array) //Generic parameter 'T' could not be inferred 

Trả lời

7

Vấn đề là, như các lỗi nói, bạn không thể sử dụng giao thức với tự hoặc các yêu cầu loại liên quan như các loại thực tế - như bạn muốn mất thông tin loại cho những yêu cầu đó. Trong trường hợp này, bạn sẽ mất thông tin loại cho các tham số của việc triển khai == - như Equatable cho biết chúng phải cùng loại với loại phù hợp (ví dụ: Self).

Giải pháp hầu như luôn luôn là để xây dựng một type eraser. Trong trường hợp các loại mong đợi bằng nhau nếu các thuộc tính id của chúng bằng nhau, điều này có thể đơn giản như việc lưu trữ thuộc tính id và so sánh nó trong việc thực hiện ==.

struct AnyVehicle : Equatable { 

    static func ==(lhs: AnyVehicle, rhs: AnyVehicle) -> Bool { 
     return lhs.id == rhs.id 
    } 

    let id : String 

    init<T : Vehicle>(_ base: T) { 
     id = base.id 
    } 
} 

(Lưu ý rằng tôi đổi tên sở hữu ID của bạn để id để phù hợp với Swift ước đặt tên)

Tuy nhiên, một giải pháp tổng quát hơn sẽ được lưu trữ một chức năng trong các loại tẩy mà có thể so sánh hai trường hợp phù hợp Vehicle tùy ý dựa trên việc thực hiện == của chúng tôi, sau khi nhập để đảm bảo chúng cùng loại với loại bê tông mà công cụ xóa kiểu đã được tạo.

struct AnyVehicle : Equatable { 

    static func ==(lhs: AnyVehicle, rhs: AnyVehicle) -> Bool { 

     // forward to both lhs's and rhs's _isEqual in order to determine equality. 
     // the reason that both must be called is to preserve symmetry for when a 
     // superclass is being compared with a subclass. 
     // if you know you're always working with value types, you can omit one of them. 
     return lhs._isEqual(rhs) || rhs._isEqual(lhs) 
    } 

    let base: Identifiable 

    private let _isEqual: (_ to: AnyVehicle) -> Bool 

    init<T : Vehicle>(_ base: T) { 

     self.base = base 

     _isEqual = { 

      // attempt to cast the passed instance to the concrete type that 
      // AnyVehicle was initialised with, returning the result of that 
      // type's == implementation, or false otherwise. 
      if let other = $0.base as? T { 
       return base == other 
      } else { 
       return false 
      } 
     } 
    } 
} 

print(AnyVehicle(Car(id: "foo")) == AnyVehicle(Tractor(id: "foo"))) // false 
print(AnyVehicle(Car(id: "foo")) == AnyVehicle(Car(id: "bar"))) // false 
print(AnyVehicle(Car(id: "foo")) == AnyVehicle(Car(id: "foo"))) // true 

var array = [AnyVehicle]() 

array.append(AnyVehicle(Car(id: "VW"))) 
array.append(AnyVehicle(Car(id: "Porsche"))) 
array.append(AnyVehicle(Tractor(id: "John Deere"))) 
array.append(AnyVehicle(Tractor(id: "Steyr"))) 

var op = Operator() 

// compiles fine as AnyVehicle conforms to Equatable. 
op.operationOnCollectionOfEquatables(array: array) 
+0

Đó là thực sự thông minh, cảm ơn bạn! Tôi sẽ thử nó để xem nó phù hợp với codebase của tôi như thế nào, nhưng tôi nghĩ đây là giải pháp phù hợp cho vấn đề trong tầm tay. Chúc mừng! –

+1

Vui lòng trợ giúp @MarkoHlebar :) – Hamish

Các vấn đề liên quan