【问题标题】:How to compare "Any" value types如何比较“任何”值类型
【发布时间】:2016-01-13 23:19:37
【问题描述】:

我有几个要比较的“Any”值类型。

var any1: Any = 1
var any2: Any = 1

var any3: Any = "test"
var any4: Any = "test"

print(any1 == any2)
print(any2 == any3)
print(any3 == any4)

使用 == 操作符会报错:

“二元运算符'=='不能应用于两个'Any'(又名 'protocol') 操作数"

这样做的方法是什么?

【问题讨论】:

  • 你怎么能比较你一无所知的东西呢?你为什么让他们Any
  • 我只是在测试 swift 的能力。
  • @theReverend === 比较引用,所以它只能应用于引用类型——它们都符合AnyObject

标签: swift


【解决方案1】:

你可以通过使用AnyHashable来做到这一点:

func equals(_ x : Any, _ y : Any) -> Bool {
    guard x is AnyHashable else { return false }
    guard y is AnyHashable else { return false }
    return (x as! AnyHashable) == (y as! AnyHashable)
}

print("\(equals(3, 4))")        // false
print("\(equals(3, equals))")   // false
print("\(equals(3, 3))")        // true

由于并非每个Equatable 都必须是Hashable,因此在极少数情况下这可能会失败。

通常没有理由使用上述 hack;但有时你会需要它,就像有时需要 AnyHashable

【讨论】:

  • 很好用。这与 Aaron Rasmussen 的回答没有什么不同(请参阅我的回答了解原因),而且它没有写成 Swiftily。
  • 这与 Aaron 的回答非常不同,因为它引入了 AnyHashable,这基本上是编译器的魔法。一旦你知道了,你当然可以用你喜欢的任何形式来写这个,例如你写的形式,也可以使用AnyHashable
  • 为了让区别更清楚:亚伦的回答说,你不能做你想做的事,你需要为此使用类型参数。我的回答是,您实际上可以做您想做的事,只需用AnyHashable 替换类型参数即可。我认为答案不会有太大的不同?
  • AnyHashable 是唯一可行的有趣的类型。在编译器魔术的帮助下。否则,您已经知道类型,然后首先没有问题。问题是,您对Any 的类型一无所知,如何比较Any 类型的两个值是否相等?这个问题的唯一实际解决方案是AnyHashable
  • 现在,您可以像在解决方案中那样做各种额外的不相关的杂技。我对此没有问题,只是不要宣传它更好,因为事实并非如此。事实上,您的解决方案是我的解决方案的复杂化,而不是 Aaron 的解决方案的扩展。
【解决方案2】:

做到这一点的唯一方法是使用除== 之外的函数,该函数接受类型参数,然后比较它们是否都是该类型的值:

func isEqual<T: Equatable>(type: T.Type, a: Any, b: Any) -> Bool {
    guard let a = a as? T, let b = b as? T else { return false }

    return a == b
}

现在,使用上面的变量,您可以像这样比较它们:

var any1: Any = 1
var any2: Any = 1

var any3: Any = "test"
var any4: Any = "test"

isEqual(type: Int.self, a: any1, b: any2)      // true
isEqual(type: Int.self, a: any2, b: any3)      // false
isEqual(type: String.self, a: any3, b: any4)   // true

【讨论】:

  • Shmart!但是是否需要传递“类型”,看起来它没有在任何地方使用。
  • 我相信尽管不使用它还是有必要的。 Swift 要求您在签名的某些部分“使用”泛型参数。但是,您不能直接将ab 转换为Equatable 吗?
  • @MichaelVoline 在函数的第一行中使用。输入被强制转换为给定的类型,如果其中一个失败,则认为它们不相等。如果类型转换成功,则相等性由为该类型实现的 == 函数确定。关键是只有比较相同类型的两个值才有意义,所以我们必须首先验证它们是否是相同的类型。但是,如果您没有将类型指定给函数,那么唯一可能的实现是将类型转换为 every 类型,直到其中一个成功,这是不可能的
【解决方案3】:

Aaron Rasmussen 的回答也可以用作扩展,如下所示:

public extension Equatable {
  /// Equate two values of unknown type.
  static func equate(_ any0: Any, _ any1: Any) -> Bool {
    guard
      let equatable0 = any0 as? Self,
      let equatable1 = any1 as? Self
    else { return false }

    return equatable0 == equatable1
  }
}
final class EquatableTestCase: XCTestCase {
  func test_equate() {
    let int: Any = Int.random( in: .min...(.max) )
    let bool: Any = Bool.random()

    XCTAssertTrue( Int.equate(int, int) )
    XCTAssertTrue( .equate(bool, bool) )
    XCTAssertFalse( .equate(int, int) )

    XCTAssertTrue( AnyHashable.equate(bool, bool) )
    XCTAssertFalse( AnyHashable.equate(bool, int) )
  }
}

【讨论】:

  • 非常优雅的代码,它可以编译,但是当我尝试复制它时它不起作用?
  • 什么是“复制”?测试通过了……
  • 在我的项目中使用它。就我而言,测试失败了?
【解决方案4】:

我们可以通过以下方式解决

enum SwiftDataType
{
    case String
    case Int
    case Int64
    case Double
    case Bool
    case Undefined
}

func getType( of : Any ) -> SwiftDataType
{
    if let type = of as? String
    {
        return SwiftDataType.String
    }
    else if let type = of as? Int
    {
        return SwiftDataType.Int
    }
    else if let type = of as? Int64
    {
        return SwiftDataType.Int64
    }
    else if let type = of as? Double
    {
        return SwiftDataType.Double
    }
    else if let type = of as? Bool
    {
        return SwiftDataType.Bool
    }
    else
    {
        return SwiftDataType.Undefined
    }
}

func isEqual( a : Any, b : Any ) -> Bool
{
    let aType : SwiftDataType = getType( of : a )
    let bType : SwiftDataType = getType( of : b )
    if aType != bType
    {
        print("Type is not Equal -> \(aType)")
        return false
    }
    else
    {
        switch aType  {
        case SwiftDataType.String :
            guard let aValue = a as? String, let bValue = b as? String else
            {
                return false
            }
            return aValue == bValue

        case SwiftDataType.Int :
            guard let aValue = a as? Int, let bValue = b as? Int else
            {
                return false
            }
            return aValue == bValue

        case SwiftDataType.Int64 :
            guard let aValue = a as? Int64, let bValue = b as? Int64 else
            {
                return false
            }
            return aValue == bValue

        case SwiftDataType.Double :
            guard let aValue = a as? Double, let bValue = b as? Double else
            {
                return false
            }
            return aValue == bValue

        case SwiftDataType.Bool :
            guard let aValue = a as?  Bool, let bValue = b as? Bool else
            {
                return false
            }
            return aValue == bValue

        default:
            return false
        }
    }
}

【讨论】:

    【解决方案5】:

    你可以使用 NSObject ...

    var any1: Any = 1
    var any2: Any = 1
    
    var any3: Any = "test"
    var any4: Any = "test"
    
    var any5: Any? = nil
    var any6: Any? = nil
    
    print(any1 as? NSObject == any2 as? NSObject)
    print(any2 as? NSObject == any3 as? NSObject)
    print(any3 as? NSObject == any4 as? NSObject)
    print(any4 as? NSObject == any5 as? NSObject)
    print(any5 as? NSObject == any6 as? NSObject)
    

    这应该产生:- 真的 错误的 真的 错误的 真的

    【讨论】:

    • 如果该类型是纯 Swift 类型,不桥接到 NSObject 等效项或不继承自 NSObject 的类?
    • 我应该添加一个警告,将您的基类设计为从 NSObject 根对象派生。我是 Swift 的新手,但我发现这样做使一些单元测试的代码更简单。否则你需要知道类名。以上仅适用于类而不适用于结构
    【解决方案6】:

    要使用== 运算符,类型必须符合Equatable 协议。 Any 协议不符合Equatable 协议,因此无法比较两个Any 值。这是合乎逻辑的 - Any 的术语过于宽泛 - 值不能有“共同点”。

    更重要的是,Swift 不允许比较两个具有不同类型的 Equatable 值。例如。 IntString 都符合 Equatable1 == "1" 不编译。原因是==Equatable 协议中的声明:func ==(lhs: Self, rhs: Self) -&gt; Bool。这个 Self 基本上意味着两个参数必须具有相同的类型。它是一种占位符 - 在特定类型的实现中,Self 应替换为此类型的名称。

    【讨论】:

    • 如果我不知道要比较的对象类型怎么办?是否可以声明任何“允许比较的通用类型”?在 Objective-C 中它更简单——所有(或大多数)对象都有共同的父 NSObject,它已经支持比较
    猜你喜欢
    • 2020-09-28
    • 1970-01-01
    • 2010-12-29
    • 1970-01-01
    • 2011-08-27
    • 1970-01-01
    相关资源
    最近更新 更多