2016-01-26 49 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一个协议扩展。

相关问题