【问题标题】:compare two properties of type Any比较 Any 类型的两个属性
【发布时间】:2018-03-23 10:06:16
【问题描述】:

我正在尝试比较一个类的两个实例的属性。我以前用过它,它工作正常:

override public func isEqual(_ object: Any?) -> Bool {
    if let rhs = object as? BankDataModel, self.accountHolderName == rhs.accountHolderName, self.accountNumber == rhs.accountNumber,
        self.accountHolderThirdPartyAccountOwner == rhs.accountHolderThirdPartyAccountOwner, self.bankName == rhs.bankName, self.bankRoutingNumber == rhs.bankRoutingNumber, self.bic == rhs.bic, self.iban == rhs.iban, self.directDebitStatus == rhs.directDebitStatus, self.sepaCreditorID == rhs.sepaCreditorID, self.sepaMandateID == rhs.sepaMandateID{
        return true
    }else{
        return false
    }
}

但是,如果我想使用它,我必须再次为我的所有模型类编写这个。我想要一个可以用于所有模型类的函数,所以我使用了 Mirror 结构并尝试了这样的方法:

 override public func isEqual(_ object: Any?) -> Bool {
    if let rhs = object as? BankDataModel{
        return compareTwoObjects(objectToCompare: rhs, objectSelf: self)
    }else{
        return false
    }
}

每个模型中重写的 isEqual 方法将从 Helper 类调用此方法,因此我可以在我拥有的每个模型中使用它:

func compareTwoObjects(objectToCompare: Any, objectSelf: Any) -> Bool{
    let objectSelf2 = Mirror(reflecting: objectSelf)
    let objectToCompare2 = Mirror(reflecting: objectToCompare)
    var index = objectToCompare2.children.startIndex
    for attr in objectSelf2.children{
        let type1 = Mirror(reflecting: attr.value).subjectType
        print(type1)
        let type2 = Mirror(reflecting: objectToCompare2.children[index].value).subjectType
        print(type2)
        if type1 == type2{
            print("Equal")
            if attr.value as! type1 != objectToCompare2.children[index].value as! type2{
                return false
            } 
        }
        index = objectToCompare2.children.index(index, offsetBy: 1)
    }
    return true
}

大部分代码工作正常,print("Equal") 总是在两个变量的类型相等时执行。 问题是这一行:

if attr.value as! type1 != objectToCompare.children[index].value as! type2{
      return false
 }

我收到此错误消息:“使用未声明的类型“type1””。 如果我不使用对 type1 和 type2 的强制转换,我会得到错误,我不能使用 == 和两个“Any”类型的属性。 有没有办法将 type1 和 type2 字符串转换为真正的数据类型?还是有更好的方法来实现我想要实现的目标?

这是我的打印输出:

Optional<String> Optional<String> Equal Optional<String> Optional<String> Equal Optional<Bool> Optional<Bool> Equal String String Equal Optional<String> Optional<String> Equal String String Equal String String Equal Optional<DirectDebitStatus> Optional<DirectDebitStatus> Equal

我将不胜感激!

【问题讨论】:

  • isEqual 用于许多函数,因此它被频繁调用并且应该尽可能高效。你这种伪泛型是不必要的昂贵。
  • 感谢您的提示。你知道实现我想做的另一种方法吗?

标签: ios swift


【解决方案1】:

斯威夫特 4

我想你可能想使用Equatable 接口。有了它,您可以实现以下目标:

extension BankDataModel: Equatable {
    static func == (lhs: BankDataModel, rhs: BankDataModel) -> Bool {
        return
            lhs.accountHolderName == rhs.accountHolderName &&
            lhs.accountNumber == rhs.accountNumber &&
            lhs.accountHolderThirdPartyAccountOwner == rhs.accountHolderThirdPartyAccountOwner
    }
}

注意:您可以使用额外的 &amp;&amp; 子句扩展 return 语句以增加“相等”检查。

然后,无论你把这个扩展放在哪里,你都可以使用== 运算符将两个BankDataModel 相互比较,如下所示:

let bankAModel: BankDataModel = // Some model
let bankBModel: BankDataModel = // Also some model

if bankAModel == bankBModel {
  // Do stuff
}

扩展

我能想到的最好的情况是你在BankDataModel 上同时实现EquatableHashable,这样它就可以在Equatable 端产生一个干净的比较方法(只比较那里的哈希值)。

这种方式会产生更好的可维护性和更简洁的代码。另见separation of concerns 模式。

【讨论】:

  • 感谢您的快速回答!抱歉信息不足,我编辑了我的问题。我的模型类都继承自 NSObject 并且 Equatable 协议已经实现。 isEqual 方法也属于 Equatable 协议。我的单元测试只需要这个来检查XCTAssertEqual(bankDataObject, bankDataObject2)。所以我不需要覆盖 == 运算符。我的问题是我想编写一个可以在每个模型类中使用的函数。所以我使用 Mirror 结构来迭代类的属性并比较它们。
  • 但这不起作用,因为我无法转换属性,也无法比较 Any 类型的两个值。
  • 我可能错过了这一点,XCTAssertEqual 正在调用下面的== 运算符来比较给定对象的相等性。还有isEqual 不是Equatable 接口的一部分吗? (见developer.apple.com/documentation/swift/equatable)。据我所知,在Equatable 接口中重载== 函数将为您在测试中运行XCTAssertEqual 提供所需的结果。
  • 如果你在你的模型中识别出共同的属性,这些属性也有一个候选的唯一键,将它们移动到一个超级模型中,你可以从中继承新的模型,比如BankDataModel然后有这个超级模型实现HashableEquatable,您应该能够在代码/测试中的任何位置相互比较所有模型。不使用任何反射。
  • 我又看了一遍,发现你是对的,对不起。但是对于 Objective C 兼容的对象类型,== 运算符已经由 isEqual 方法提供。不幸的是,我的属性非常不同,所以我不能为它们使用好的超类..
【解决方案2】:

我遇到了类似的问题。需要比较我从不同来源获得的几个(巨大的)模型对象。从您的代码开始并通过递归和特殊情况进行扩展,我最终得到了以下代码 sn-p。

(请注意,我在 Test 类中使用它,因此不需要高效。)

func compareProperties(of target1: Any, with target2: Any) -> Bool {
    
    let mirror1 = Mirror(reflecting: target1)
    let mirror2 = Mirror(reflecting: target2)
    var index = mirror1.children.startIndex
    
    for child in mirror1.children {
        let child2 = mirror2.children[index]
        
        //Make another mirror to see if it has children
        let innerMirror1 = Mirror(reflecting: child.value)
        
        if (innerMirror1.children.count > 0) {
            // Use recursive reflection on the value of each child.
            print("Recursion on \(innerMirror1.subjectType)")
            if (!compareProperties(of: child.value, with: child2.value)) {
                return false
            }
        } else {
            //Compare the values
            let type1 = innerMirror1.subjectType
            let innerMirror2 = Mirror(reflecting: child2.value)
            let type2 = innerMirror2.subjectType
            if type1 == type2 {
                
                let stringValue1 = "\(child.value)"
                let stringValue2 = "\(child2.value)"
                
                //print("\(type1): \(stringValue1) ==? \(type2): \(stringValue2)")
                                                
                if (stringValue1 != stringValue2) {
                    print("\(type1): \(stringValue1) != \(type2): \(stringValue2)")
                    //Special case with Dates
                    if ("\(mirror1.subjectType)" == "Date") {
                        //Skip comparison of Doubles in Dates
                        if ("\(target1 as? Date)" != "\(target2 as? Date)") {
                            return false
                        }
                    } else {
                        return false
                    }
                }
                
            } else {
                print("Different classes \(type1) and \(type2)")
                return false
            }
        }
        //Increase index for next value pair
        index = mirror1.children.index(index, offsetBy: 1)
    }
    //If we get here it was never false.
    return true
}

【讨论】:

    【解决方案3】:

    这可能是您要查找的内容,它通过首先将两个 Any? 对象转换为 NSObject 来比较它们。

        private func equalAny<BaseType: Equatable>(lhs: Any?, rhs: Any?, baseType: BaseType.Type) -> Bool {
        if (lhs == nil && rhs != nil) || (rhs == nil && lhs != nil) { return false }
        if lhs == nil && rhs == nil { return true }
        guard let lhsEquatable = lhs as? BaseType, let rhsEquatable = rhs as? BaseType else {
            return false
        }
        return lhsEquatable == rhsEquatable
    }
    

    确保使用NSObject 作为基本类型。像这样调用方法:

    equalAny(lhs: value, rhs: otherValue, baseType: NSObject.self)

    【讨论】:

    • 谢谢,太好了。唯一的问题是当我在isEqual() 中调用它时,isEqual() 会因为return lhsEquatable == rhsEquatable 而再次被调用。所以我有一个无限循环......
    猜你喜欢
    • 1970-01-01
    • 2014-03-22
    • 2011-06-23
    • 2015-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多