2016-01-26 50 views
2

假設我們有一個相當大的struct斯威夫特:有沒有辦法在Swift中爲`struct`自動定義compare(`==`)函數?

struct SuperStruct { 
    var field1: Int = 0 
    var field2: String = "" 
    // lots of lines... 
    var field512: Float = 0.0 
} 

..然後我們需要實現Equatable協議:

extension SuperStruct: Equatable { 
} 

func ==(lhs: SuperStruct, rhs: SuperStruct) -> Bool { 
    return 
     lhs.field1 == rhs.field1 && 
     lhs.field2 == rhs.field2 && 
     // lots of lines... 
     lhs.field512 == rhs.field512 
} 

...我們需要寫大量的行愚蠢的代碼。 有沒有辦法「讓」編譯器「爲我們做」呢?

+0

你應該看看協議擴展。 WWDC 2015 Session 408. http://devstreaming.apple.com/videos/wwdc/2015/408509vyudbqvts/408/408_hd_protocoloriented_programming_in_swift.mp4?dl=1 –

+0

你會多麼努力編寫一個小幫手來解析一段代碼並生成所需的方法?在上面添加bean的版本,以跟蹤可能需要更新的代碼,並且你很好。 –

+0

Leo Dabus,謝謝!我知道協議擴展如何工作,但我不知道如何將它用於我的問題。 –

回答

3

以下回答顯示一個可能解決方案;可能不是推薦的一個(但對未來這個問題的讀者可能會感興趣)。


如果您有大量這都屬於一個比較有限數量的不同類型的屬性,你可以使用你的結構實例的Mirror和迭代在結構的屬性;對於每個嘗試轉換到不同類型的,您知道您的屬性爲

我已經編輯了以前的答案(的東西,我相信頗多整潔),看下面的WWDC 2015年會議結束後(感謝獅子座Dabus!):

我會離開這個答案底部的初步回答爲好,因爲它顯示了一種替代,面向協議的方法少,利用這一Mirror解決方案。

Mirror &面向協議的解決方案:

/* Let a heterogeneous protocol act as "pseudo-generic" type 
    for the different (property) types in 'SuperStruct'   */ 
protocol MyGenericType { 
    func isEqualTo(other: MyGenericType) -> Bool 
} 
extension MyGenericType where Self : Equatable { 
    func isEqualTo(other: MyGenericType) -> Bool { 
     if let o = other as? Self { return self == o } 
     return false 
    } 
} 

/* Extend types that appear in 'SuperStruct' to MyGenericType */ 
extension Int : MyGenericType {} 
extension String : MyGenericType {} 
extension Float : MyGenericType {} 
    // ... 

/* Finally, 'SuperStruct' conformance to Equatable */ 
func ==(lhs: SuperStruct, rhs: SuperStruct) -> Bool { 

    let mLhs = Mirror(reflecting: lhs).children.filter { $0.label != nil } 
    let mRhs = Mirror(reflecting: rhs).children.filter { $0.label != nil } 

    for i in 0..<mLhs.count { 
     guard let valLhs = mLhs[i].value as? MyGenericType, valRhs = mRhs[i].value as? MyGenericType else { 
      print("Invalid: Properties 'lhs.\(mLhs[i].label!)' and/or 'rhs.\(mRhs[i].label!)' are not of 'MyGenericType' types.") 
      return false 
     } 
     if !valLhs.isEqualTo(valRhs) { 
      return false 
     } 
    } 
    return true 
} 

實例:

/* Example */ 
var a = SuperStruct() 
var b = SuperStruct() 
a == b // true 
a.field1 = 2 
a == b // false 
b.field1 = 2 
b.field2 = "Foo" 
a.field2 = "Foo" 
a == b // true 

上一頁Mirror溶液:

/* 'SuperStruct' conformance to Equatable */ 
func ==(lhs: SuperStruct, rhs: SuperStruct) -> Bool { 

    let mLhs = Mirror(reflecting: lhs).children.filter { $0.label != nil } 
    let mRhs = Mirror(reflecting: rhs).children.filter { $0.label != nil } 

    for i in 0..<mLhs.count { 
     switch mLhs[i].value { 
     case let valLhs as Int: 
      guard let valRhs = mRhs[i].value as? Int where valRhs == valLhs else { 
       return false 
      } 
     case let valLhs as String: 
      guard let valRhs = mRhs[i].value as? String where valRhs == valLhs else { 
       return false 
      } 
     case let valLhs as Float: 
      guard let valRhs = mRhs[i].value as? Float where valRhs == valLhs else { 
       return false 
      } 
      /* ... extend with one case for each type 
      that appear in 'SuperStruct' */ 
     case _ : return false 
     } 
    } 
    return true 
} 

用法示例:

/* Example */ 
var a = SuperStruct() 
var b = SuperStruct() 
a == b // true 
a.field1 = 2 
a == b // false 
b.field1 = 2 
b.field2 = "Foo" 
a.field2 = "Foo" 
a == b // true 
+1

哇,很好的黑客。 +1。可怕的,並可能證明一個可能*不應該*試圖避免直接寫「可衡量」一致性。 – rickster

+1

太棒了!可能它不是生產解決方案(它應該很慢,我相信),但我肯定可以在開發中使用它,而SuperStruct中的一組屬性不穩定。 –

+0

如何讓Array符合MyGenericType? – tungsten

1

不,不。至少不會以過於複雜的方式,並且基於運行時反思的使用(濫用?)。請參閱dfri's answer以瞭解技術上的工作原理,但這是方式比直接比較所有字段的==實現更復雜。

至於你對Swift中「應該」可用的意見,如果你分享with Apple或​​,你更可能會看到一些效果。

1

你可以使struct Codable和比較JSON編碼的數據。效率不高,但可用於某些應用程序(例如單元測試)。

struct SuperStruct: Encodable { 
    var field1: Int = 0 
    // .... 
    var field512: Float = 0.0 
} 

let s1 = SuperStruct() 
let s2 = SuperStruct() 

let encoder = JSONEncoder() 
let data1 = try! encoder.encode(s1) 
let data2 = try! encoder.encode(s2) 
let result = (data1 == data2) 

如果你喜歡這個,你可以整理一下成Encodable一個協議擴展。

相關問題