【问题标题】:AnyHashable as an alternative to AnyEquatableAnyHashable 作为 AnyEquatable 的替代品
【发布时间】:2020-04-24 01:41:52
【问题描述】:

我需要比较符合P 协议的结构数组。

P 不能符合 Equatable,因为它不能有“自我要求”。

创建AnyEquatable 作为类型擦除是用于此目的的常见做法。 但是,AnyHashable 已经是标准库的一部分,它符合Equatable

我想知道AnyEquatable 是否有充分的理由不属于标准库。 是否应该使用标准AnyHashable 来代替AnyEquatable

【问题讨论】:

    标签: swift type-erasure


    【解决方案1】:

    AnyHashable 包含了一堆常用功能。 AnyEquatable 没有;它的作用可以用一个闭包来表示。

    let cupcake = "?"
    let notCake = 0xca_e
    
    let cupcakeEquals: (Any) -> Bool = try cupcake.getEquals()
    XCTAssert( cupcakeEquals(cupcake) )
    XCTAssertFalse( cupcakeEquals(notCake) )
    
    let notCakeEquals = try notCake.getEquals(Any.self)
    XCTAssert( notCakeEquals(notCake) )
    XCTAssertFalse( notCakeEquals(cupcake) )
    
    XCTAssertThrowsError( try cupcake.getEquals(Int.self) )
    
    public extension Equatable {
      /// A closure that equates another instance to this intance.
      /// - Parameters:
      ///   - _: Use the metatype for `Castable` to avoid explicit typing.
      /// - Throws: `CastError.impossible` if a `Castable` can't be cast to `Self`.
      func getEquals<Castable>(_: Castable.Type = Castable.self) throws -> (Castable) -> Bool {
        if let error = CastError(self, desired: Castable.self)
        { throw error }
    
        return { self == $0 as? Self }
      }
    }
    
    /// An error that represents casting gone wrong. ?‍♀️?
    public enum CastError: Error {
      /// An undesired cast is possible.
      case possible
    
      /// An desired cast is not possible.
      case impossible
    }
    
    public extension CastError {
      /// `nil` if  an `Instance` can be cast to `Desired`. Otherwise, `.impossible`.
      init?<Instance, Desired>(_: Instance, desired _: Desired.Type) {
        self.init(Instance.self, desired: Desired.self)
      }
    
      /// `nil` if  a `Source` can be cast to `Desired`. Otherwise, `.impossible`.
      init?<Source, Desired>(_: Source.Type, desired _: Desired.Type) {
        if Source.self is Desired.Type
        { return nil }
    
        self = .impossible
      }
    
      /// `nil` if  an `Instance` cannot be cast to `Undesired`. Otherwise, `.possible`.
      init?<Instance, Undesired>(_: Instance, undesired _: Undesired.Type) {
        self.init(Instance.self, undesired: Undesired.self)
      }
    
      /// `nil` if  a `Source` cannot be cast to `Undesired`. Otherwise, `.possible`.
      init?<Source, Undesired>(_: Source.Type, undesired _: Undesired.Type) {
        guard Source.self is Undesired.Type
        else { return nil }
    
        self = .possible
      }
    }
    

    你可以包装它,以符合EquatableCastAny。有用例吗?

    /// A type-erased equatable value.
    ///
    /// An `Equatable` instance is stored as a "`Cast`".
    /// Only instances that can be cast to that type can be `==`'d with the `AnyEquatable`.
    public struct AnyEquatable<Cast> {
      public init<Equatable: Swift.Equatable>(_ equatable: Equatable) throws {
        equals = try equatable.getEquals()
        cast = equatable as! Cast
      }
    
      private let equals: (Cast) -> Bool
      private let cast: Cast
    }
    
    extension AnyEquatable: Equatable {
      public static func == (equatable0: Self, equatable1: Self) -> Bool {
        equatable0 == equatable1.cast
      }
    }
    
    public extension AnyEquatable {
      static func == (equatable: Self, castable: Cast) -> Bool {
        equatable.equals(castable)
      }
    
      static func == (castable: Cast, equatable: Self) -> Bool {
        equatable.equals(castable)
      }
    }
    
    let anyEquatable = try AnyEquatable<Any>(cupcake)
    XCTAssertEqual( anyEquatable, try .init(cupcake) )
    XCTAssert(anyEquatable == cupcake)
    XCTAssertFalse(notCake == anyEquatable)
    

    【讨论】:

    • 谢谢,但这并不能解释为什么不使用 AnyHashable 而不是自定义类型擦除(可能更具体),如 AnyEquatable(或您建议的其他风格)。你认为你的建议比使用 AnyHashable 更简单/更好/更容易吗?而且,为什么 AnyEquatable 不是标准库的一部分?
    • 我是说没有用例; getEquals 可能超出了需要。但是,如果您有用例,请显示用例! AnyHashable 是你用来擦除 Hashable 东西的东西,因为它们有关联的属性和方法。我是说拥有类型不值得,因为归结为操作员。我认为您只是在查看AnyHashable,因为它已经存在,而不是因为它几乎包含您实际想要的那么少。
    • AnyHashable 也被编译器优化。避免将其包装在盒子中有点神奇。据我所知,它主要用于 NSDictionary 到 Dictionary 的桥接。如果你发现你需要很多 AnyEquatable 或 AnyHashable 与桥接到 Cocoa 无关,那么你可能误用了 IMO 类型系统。一般来说,你不应该让协议要求 Equatable;如有必要,您应该添加自定义 .isEqual 要求。我在 37:44 左右讨论这个问题:youtube.com/watch?v=DXwJg0QTlZE
    猜你喜欢
    • 2021-09-27
    • 2012-09-26
    • 2012-08-19
    • 2010-10-16
    • 1970-01-01
    • 2022-08-23
    • 2019-04-04
    • 2017-11-19
    • 1970-01-01
    相关资源
    最近更新 更多