Сравнение общих типов структур
Как определить, имеют ли два экземпляра общей структуры одного типа?
Например, с учетом следующей структуры:
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" )
Ожидаемый идентификатор в точечном типе
и
Ожидаемое выражение
Ответы
Ответ 1
Взяв некоторое вдохновение из ответа Себастьяна, я придумал это решение:
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)
}
Но у него есть некоторые недостатки, о которых упоминал Себастьян. А именно, если вы извлекли свои значения из коллекции Objective-C, они оба будут иметь тип AnyObject
. Более того, если мы вложим мою функцию sameType
в функцию, аргументы которой не являются дженериками, например:
func compare(foo: Any, bar: Any) -> Bool {
return sameType(foo, bar)
}
Функция sameType
здесь всегда будет возвращать true
. Тип foo
и bar
, что касается sameType
, это Any
, а не какой-либо конкретный тип I, который мог бы скрыть его.
Также, вероятно, стоит отметить, что если я попытаюсь вставить это как метод экземпляра, произойдет сбой 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
Изменить:
Извините за преждевременный ответ, он на самом деле не работает, потому что компилятор будет выбирать функцию для разных типов при вызове из другой функции:
func foobar<T,U> (lhs: Foo<T>, rhs: Foo<U>) -> Bool {
return lhs.sameType(rhs)
}
Если вы останетесь на чистой территории Свифта, будет работать следующее:
Учитывая простую общую структуру
struct Foo<T> {
let v : T
}
Вы можете определить функцию sameType
, которая принимает значение Foo
того же типа и просто возвращает true
:
func sameType<T> (a: Foo<T>, b: Foo<T>) -> Bool {
return true
}
и перегрузите функцию двумя разными Foo
s:
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
Это работает аналогично с методами экземпляра в struct:
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>
Ответ 3
Если я не понял вас неправильно, вы не хотите знать, имеют ли переменные типа FooBar
одного типа (потому что они есть), вы хотите проверить, используют ли они один и тот же общий тип.
Так как структура уже содержит свойство универсального типа, вы можете использовать его для сравнения типов, а не с помощью самой структуры:
func areExactType<U>(x: FooBar<U>) -> Bool {
return x.variable is T
}
Я тестировал на игровой площадке, и он работает с базовыми типами данных, массивами, словарями и т.д.
Недостатком является то, что для того, чтобы он работал, структура должна обладать свойством родового типа.