AnyObject尝试强制转换为Equatable

时间:2017-08-31 22:12:54

标签: swift swift-protocols equatable

我有Equatable

class Item: Equatable {
    var value: AnyObject?
    var title: String
    init(title: String, value: AnyObject?) {
        self.title = title
        self.value = value
    }
    //Equatable
    public static func ==(lhs: Item, rhs: Item) -> Bool {
        return ((lhs.title == rhs.title) && (lhs.value === rhs.value))
    }  
}

但我希望将try value转换为Equatable,以便获得软等同的结果

if let lValue = lhs.value as? Equatable,   // Error
   let rValue = rhs.value as? Equatable {  // Error
    valueEq = (lValue == rValue)
} else {
    valueEq = (lhs.value === rhs.value)
}
  

此代码捕获有关Generic Equatable

的编译错误

我怎么能为这个班做正确的Equatable?

UPD

我想在我的故事板中使用Item中的UITableViewCell。我不能创建通用UITableViewCell。如果我尝试将Item作为Generic<T: Equatable>类,我将被迫在我的单元格中指定类型,

var items: [Item<OnlyThisHashableClass>]

但我想在单元格中使用Item中的任何对象

2 个答案:

答案 0 :(得分:2)

您无法将AnyObject投射到Equatable

您可以做的是将Item定义为valueWrapped必须为Equatable的通用:

class Item<Wrapped: Equatable> {
    var title: String
    var value: Wrapped

    init(title: String, value: Wrapped) {
        self.title = title
        self.value = value
    }
}

extension Item: Equatable {
    static func ==(lhs: Item, rhs: Item) -> Bool {
        return lhs.title == rhs.title && lhs.value == rhs.value
    }
}

而且,让我们假设你有一些类Foo,(a)是不等的; (b)是你要用Item包裹的东西; (c)你真的想在身份运算符===的基础上将它们定义为等同的。 (我承认,我发现这个概念,你称之为“软等于”相当令人不安的概念,但我不会在这里讨论。)

无论如何,你可以在身份运算符的基础上使你的班级Foo相等:

extension Foo: Equatable {
    static func ==(lhs: Foo, rhs: Foo) -> Bool {
        return lhs === rhs
    }
}

或者,如果您需要为许多类执行此操作,您甚至可以拥有此身份相等的协议,然后您的非等同类可以符合:

protocol IdentityEquatable: class, Equatable { }

extension IdentityEquatable {
    static func ==(lhs: Self, rhs: Self) -> Bool {
        return lhs === rhs
    }
}

然后,您希望在Item中包装的非Equatable的任何类都可以采用此标识等同的行为,每行只有一行代码:

extension Foo: IdentityEquatable { }
extension Bar: IdentityEquatable { }
extension Baz: IdentityEquatable { }

顺便说一下,SE-0143已被批准,虽然还不是该语言的一部分,但在未来的Swift版本中提供了条件一致性的承诺,即:

class Item<Wrapped> {
    var title: String
    var value: Wrapped

    init(title: String, value: Wrapped) {
        self.title = title
        self.value = value
    }
}

extension Item: Equatable where Wrapped: Equatable {
    static func ==(lhs: Item, rhs: Item) -> Bool {
        return lhs.title == rhs.title && lhs.value == rhs.value
    }
}

在这种情况下,Item当且仅当Equatable值为Wrapped时才为Equatable。这还不是该语言的一部分,但看起来它将在未来的版本中。对于这个问题,这是一个优雅的解决方案(尽管不是,不可否认,你的“软等同”的想法)。

答案 1 :(得分:1)

简单方法 - 类保持NonGeneric,Generic only init,并在GenericInit中创建isEquals方法

class FieldItem: CustomStringConvertible, Equatable {
    let value: Any?
    let title: String
    private let equals: (Any?) -> Bool
    init<Value: Equatable>(title: String, value: Value?) {
        func isEquals(_ other: Any?) -> Bool {
            if let l = value, let r = other {
                if let r = r as? Value {
                    return l == r
                } else {
                    return false
                }
            } else {
                return true
            }
        }
        self.title = title
        self.value = value
        self.equals = isEquals
    }
    //CustomStringConvertible
    var description: String { get { return title } }
    //Equatable
    public static func ==(lhs: FieldItem, rhs: FieldItem) -> Bool {
        return ((lhs.title == rhs.title) && lhs.equals(rhs.value))
    }

}
相关问题