比较通用的结构体类型

34

如何确定两个泛型结构体实例是否属于同一类型?

例如,给定以下结构体:

struct FooBar<T> {
    let variable: T
    init(arg: T) {
        variable = arg
    }
}

以下是代码片段:

并且以下是代码片段:

let foo = FooBar(1)
let bar = FooBar(1.0)
let baz = FooBar("1")

我该如何确定 foo, bar 或者 baz 是否属于同一类型或不同类型?
func areExactType(x: FooBar) -> Bool {
    return self.dynamicType === x.dynamicType
}

这个错误提示是因为类型 'Foo' 不符合协议 'AnyObject' 的要求。

func areExactType(x: FooBar) -> Bool {
    return self.dynamicType === x.dynamicType
}

这会导致

无法使用参数列表类型为'(Foo.Type, Foo.Type)'的'=='


func areExactType(x: FooBar) -> Bool {
    return self is x.dynamicType
}

这里有三个错误:

同一行上连续的语句必须用 ';' 分隔开

(这里需要在句号和 'dynamicType' 之间加上分号)

点类型中缺少标识符

还有

表达式不完整


5
我在谷歌搜索后找到了这个问题。未来的我感谢过去的我。 - nhgrif
3个回答

13

编辑:

很抱歉之前的回答是不正确的,因为当在另一个函数中调用时,编译器会为不同的类型选择不同的函数:

func foobar<T,U> (lhs: Foo<T>, rhs: Foo<U>) -> Bool {
    return lhs.sameType(rhs)
}

如果您使用纯Swift语言,以下内容将可行:
给定一个简单的泛型结构体。
struct Foo<T> {
    let v : T
}

您可以定义一个名为sameType的函数,它接受两个相同类型的Foo,并返回true
func sameType<T> (a: Foo<T>, b: Foo<T>) -> Bool {
    return true
}

并使用两个不同的Foo来重载函数:

func sameType<T,U> (a: Foo<T>, b: Foo<U>) -> Bool {
    return false;
}

编译器将根据参数类型选择一个方法:
let a = Foo(v: 1.0)
let b = Foo(v: "asdf")
sameType(a, b) // false
sameType(a, a) // true

这同样适用于结构体上的实例方法:
    func sameType (other: Foo) -> Bool {
        return true
    }

    func sameType<U> (other: Foo<U>) -> Bool {
        return false
    }

如果你混用Swift和Objective-C,或者出于其他原因需要依赖动态类型,则可能会导致意想不到的结果:

import Foundation
let x = NSArray(object: 1)
let y = NSArray(object: "string")
sameType(Foo(v: x[0]), Foo(v: y[0])) // true

结果是正确的,因为because Foo(v: x[0])的类型是Foo<AnyObject>

6
它只在非常有限的情况下有效,否则毫无用处。我可以给自己的回答点个踩吗? - Sebastian
仍然可以在某些特定情况下有用。 - David Gomez
@Sebastian,我发现了你回答中的一个小改进。 - nhgrif
似乎在Swift 3.0中无法工作:“参数标签与可用的重载不匹配。” - Frederick C. Lee
对我来说,你的回答有点令人困惑。我无法理解哪部分是正确的,哪部分是错误的。你故意写错了哪一部分,哪一部分是为了指出缺陷而故意写错的?你能修改一下吗? - mfaani

7

Sebastian的回答中得到一些灵感,我想出了这个解决方案:

func sameType<L,R>(left: L, right: R) -> Bool {
    if let cast = left as? R {
        return true
    } else {
        return false
    }
}

即使嵌套在接受泛型的函数中,这也是有效的代码:
func compare<T,U>(foo: T, bar: U) -> Bool {
    return sameType(foo, bar)
}

但它确实有一些Sebastian提到的缺点。即,如果您从Objective-C集合中检索了值,它们都将具有 AnyObject 类型。此外,如果我们将我的 sameType 函数嵌套在参数不是泛型的函数内部,例如:

func compare(foo: Any, bar: Any) -> Bool {
    return sameType(foo, bar)
}

这里的sameType函数将始终返回true。 就sameType所涉及的foobar类型而言,它们的类型是Any,而不是我可能向下转换到的任何最具体的类型。
值得注意的是,如果我将其嵌套为实例方法,Xcode 可能会崩溃。再读一遍,Xcode 会崩溃,而不是“产生不良结果”。 应用程序会崩溃。
例如:
func sameType<L,R>(left: L, right: R) -> Bool {
    if let cast = left as? R {
        return true
    } else {
        return false
    }
}

struct Foo<T> {
    func sameType<U>(bar: U) -> Bool {
        return sameType(self, bar)
    }
}

let x = Foo<Int>()
let y = Foo<Float>()

if x.sameType(y) {
    println("x is a y")
}

这段代码片段会导致Xcode崩溃。我不知道为什么会这样,但确实会发生...
如果我们改为这样写:
if sameType(x,y) {
    println("x is a y")
}

Xcode编译和运行都很好。

2
如果我没有理解错误,您不想知道类型为FooBar的变量是否属于相同类型(因为它们是相同的),而是想检查它们是否使用了相同的泛型类型。
由于该结构已经包含一个泛型类型的属性,您可以使用它来进行类型比较,而不是使用该结构本身:
func areExactType<U>(x: FooBar<U>) -> Bool {
    return x.variable is T
}

我已在一个playground中进行了测试,并且它可以处理基本数据类型、数组、字典等。 缺点是为了使其工作,结构体必须具有泛型类型的属性。


使用结构体本身可以很好地区分Foo<T>和Foo<U>之间的差异。 - nhgrif
是的,我同意这一点,但最终你要找到的是 T == U 是否成立。这只是解决问题的不同方式。我看到你没有标记任何答案为解决方案,所以我认为你正在寻找其他选择。 - Antonio
但是如果你的结构体具有多个泛型类型,那么你的方式很快就会变得复杂起来。此外,如果该类型不是公共可访问的变量,那又该怎么办呢? - nhgrif
答案基于您提供的示例代码 - 它不是通用的,我同意当泛型超过一个时会变得复杂。至于可访问性,私有变量是可访问的,只要 x 参数是相同类型的(在我们的情况下始终如此)。 - Antonio

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接