Как развернуть необязательное значение из любого типа?
Учитывая массив [Any]
, который содержит сочетание необязательных и необязательных значений, например:
let int:Int? = 1
let str:String? = "foo"
let values:[Any] = [int,2,str,"bar"]
Как мы можем извлечь значение Optional
в типе Any
(если оно есть), чтобы мы могли создать общую функцию печати, которая выводит только значения.
например. эта функция printArray проходит и печатает каждый элемент:
func printArray(values:[Any]) {
for i in 0..<values.count {
println("value[\(i)] = \(values[i])")
}
}
printArray(values)
Будет выводиться:
value[0] = Optional(1)
value[1] = 2
value[2] = Optional("foo")
value[3] = bar
Как мы можем изменить его, чтобы он печатал только базовое значение, чтобы оно разворачивало значение, если оно необязательно? например:
value[0] = 1
value[1] = 2
value[2] = foo
value[3] = bar
Обновление прогресса...
Он может работать при изменении аргумента на [Any?]
, например:
let values:[Any?] = [int,2,str,"bar"]
func printArray(values:[Any?]) {
for i in 0..<values.count {
println("value[\(i)] = \(values[i]!)")
}
}
printArray(values)
Будет напечатано желаемое:
value[0] = 1
value[1] = 2
value[2] = foo
value[3] = bar
Но хотелось бы посмотреть, как мы можем развернуть опцию из Any
, так как это возвращает MirrorType.value
, что затрудняет извлечение необязательного значения, например:
class Person {
var id:Int = 1
var name:String?
}
var person = Person()
person.name = "foo"
var mt:MirrorType = reflect(person)
for i in 0 ..< mt.count {
let (name, pt) = mt[i]
println("\(name) = \(pt.value)")
}
Распечатывает:
id = 1
name = Optional("foo")
Когда мне нужно:
id = 1
name = foo
Ответы
Ответ 1
Для Xcode 7 и Swift 2:
func unwrap(any:Any) -> Any {
let mi = Mirror(reflecting: any)
if mi.displayStyle != .Optional {
return any
}
if mi.children.count == 0 { return NSNull() }
let (_, some) = mi.children.first!
return some
}
let int:Int? = 1
let str:String? = "foo"
let null:Any? = nil
let values:[Any] = [unwrap(int),2,unwrap(str),"bar", unwrap(null)]
Это даст вам [1, 2, "foo", "bar", {NSObject}]
Измените NSNull()
на nil
, а возвращаемое значение функции разворота на Any?
всегда будет распаковывать любой тип.
Ответ 2
Я думаю, что это своего рода ошибка.
В общем случае, чтобы обнаружить и извлечь конкретный тип из Any
, downing с as
является единственным поддерживаемым методом. Но:
let int:Int? = 1
let any:Any = int
switch any {
case let val as Optional<Int>: // < [!] cannot downcast from 'Any' to a more optional type 'Optional<Int>'
print(val)
default:
break
}
Это означает, что этого не существует.
В любом случае, видимо, вы можете сделать это с помощью reflect
:
func printArray(values:[Any]) {
for i in 0..<values.count {
var val = values[i]
var ref = reflect(val)
// while `val` is Optional and has `Some` value
while ref.disposition == .Optional && ref.count > 0 && ref[0].0 == "Some" {
// replace `val` with unwrapped value
val = ref[0].1.value;
ref = reflect(val)
}
println("value[\(i)] = \(val)")
}
}
let int:Int? = 1
let str:String? = "foo"
let values:[Any] = [int,2,str,"bar"]
printArray(values)
выходы:
value[0] = 1
value[1] = 2
value[2] = foo
value[3] = bar
ДОБАВЛЕНО: небольшая измененная версия
func printArray(values:[Any]) {
for i in 0..<values.count {
var ref = reflect(values[i])
// while `val` is Optional and has `Some` value
while ref.disposition == .Optional && ref.count > 0 && ref[0].0 == "Some" {
// Drill down to the Mirror of unwrapped value
ref = ref[0].1
}
let val = ref.value
println("value[\(i)] = \(val)")
}
}
Факторинг в функцию:
func unwrapAny(val:Any) -> Any {
var ref = reflect(val)
while ref.disposition == .Optional && ref.count > 0 && ref[0].0 == "Some" {
ref = ref[0].1
}
return ref.value
}
func printArray(values:[Any]) {
for i in 0..<values.count {
println("value[\(i)] = \(unwrapAny(values[i]))")
}
}
Ответ 3
Чтобы проверить, является ли переменная Any
опциональной протокол может использоваться как средство необязательного Необязательного.
Так же, как его в настоящее время невозможно (как и для Swift 2), чтобы проверить на отсутствие Необязательный Необязательно, также невозможно включить a в необязательный вариант:
let anyType: Any.Type = Optional<String>.self
let anyThing: Any = Optional.Some("string")
anyType is Optional.Type // Causes error
let maybeString = anything as? Optional // Also causes error
// Argument for generic parameter 'Wrapped' could not be inferred
Однако предлагаемый OptionalProtocol
также может использоваться для предоставления универсального интерфейса для доступа к необязательным значениям и даже разворачивания их:
protocol OptionalProtocol {
func isSome() -> Bool
func unwrap() -> Any
}
extension Optional : OptionalProtocol {
func isSome() -> Bool {
switch self {
case .None: return false
case .Some: return true
}
}
func unwrap() -> Any {
switch self {
// If a nil is unwrapped it will crash!
case .None: preconditionFailure("nill unwrap")
case .Some(let unwrapped): return unwrapped
}
}
}
// With this we can check if we have an optional
let maybeString: String? = "maybe"
let justString: String = "just"
maybeString is OptionalProtocol // true
justString is OptionalProtocol // false
Благодаря предоставленным методам опциональные опции можно проверить и получить доступ совершенно естественным образом, не требуя невозможного нажатия на Optional
:
let values:[Any] = [
Optional.Some(12),
2,
Optional<String>.None, // a "wrapped" nil for completeness
Optional.Some("maybe"),
"something"
]
for any in values {
if let optional = any as? OptionalProtocol {
if optional.isSome() {
print(optional.unwrap())
} else {
// nil should not be unwrapped!
print(optional)
}
continue
}
print(any)
}
Что будет печатать:
12
2
nil
maybe
something
Ответ 4
Чтобы, возможно, спасти кого-то от вымотания всего этого из ответов и комментариев, вот ответ, включающий как "нормальные" способы, так и некоторые, что я считаю улучшением для Swift 3, идущего с Xcode 8.2.1.
Использование Reflection
func unwrap<T>(_ any: T) -> Any
{
let mirror = Mirror(reflecting: any)
guard mirror.displayStyle == .optional, let first = mirror.children.first else {
return any
}
return first.value
}
Обсуждение
Принятый ответ от bubuxu не скомпилируется с Swift 3.
Как говорится в его комментариях, изменение .Optional
до .Optional
исправляет это (см. SE-0005 и Руководство по разработке Swift API).
Причины, по которым я думал, что это решение можно улучшить:
- Я нахожу возвращение
NSNull()
странным.
- Я думаю, что альтернатива возврата
nil
с типом возврата Any?
также проблематична, потому что она превращает все (включая необязательные значения) в необязательные значения
(например, unwrap(any: 42)
возвращает Optional(42)
).
- При вызове
unwrap(any:)
с чем-либо, кроме значения Any
(более того, кто-нибудь?) компилятор Swift 3 предупреждает о неявно
принуждение к Any
.
Символьные мысли касаются ответа Сайджона.
Решение, которое я предлагаю, адресует все эти точки. Имейте в виду, однако, что unwrap(_:)
возвращает nil
как тип Any
, поэтому используя nil
коалесцирующий оператор больше не работает. Это означает, что это просто смещается вокруг того, что, по моему мнению, проблематично во втором вопросе. Но я нашел, что это было правильное решение для (для меня) более интересного случая использования рефлексии.
Использование расширения по выбору
protocol OptionalProtocol {
func isSome() -> Bool
func unwrap() -> Any
}
extension Optional : OptionalProtocol {
func isSome() -> Bool {
switch self {
case .none: return false
case .some: return true
}
}
func unwrap() -> Any {
switch self {
case .none: preconditionFailure("trying to unwrap nil")
case .some(let unwrapped): return unwrapped
}
}
}
func unwrapUsingProtocol<T>(_ any: T) -> Any
{
guard let optional = any as? OptionalProtocol, optional.isSome() else {
return any
}
return optional.unwrap()
}
Обсуждение
Это базовое решение LopSae обновлено до Swift 3. Я также изменил сообщение об ошибке предварительного условия и добавил unwrapUsingProtocol(_:)
.
Использование
class Person {
var id:Int = 1
var name:String?
}
var person = Person()
person.name = "foo"
let mirror = Mirror(reflecting: person)
for child in mirror.children.filter({ $0.label != nil }) {
print("\(child.label!) = \(unwrap(child.value))")
}
Независимо от того, используете ли вы unwrap()
или unwrapUsingProtocol()
, это напечатает
id = 1
name = foo
Если вы ищете способ аккуратного выравнивания вывода, см. Есть ли способ использовать вкладки для равномерного выделения строк описания в Swift?
Ответ 5
Не полный ответ. Это сводится к следующему:
let int:Int? = 1
let str:String? = "foo"
let values:[Any] = [int,2,str,"bar"]
func printArray(values:[Any]) {
for i in 0..<values.count {
let v = values[i]
if _stdlib_demangleName(_stdlib_getTypeName(v)) == "Swift.Optional" {
println("value[\(i)] = "it optional: \(v)") // here I'm stuck
}else {
println("value[\(i)] = \(values[i])")
}
}
}
printArray(values)
Ответ 6
Как насчет этого решения, я сделал общую версию предыдущего ответа.
fileprivate func unwrap<T>(value: Any)
-> (unwraped:T?, isOriginalType:Bool) {
let mirror = Mirror(reflecting: value)
let isOrgType = mirror.subjectType == Optional<T>.self
if mirror.displayStyle != .optional {
return (value as? T, isOrgType)
}
guard let firstChild = mirror.children.first else {
return (nil, isOrgType)
}
return (firstChild.value as? T, isOrgType)
}
let value: [Int]? = [0]
let value2: [Int]? = nil
let anyValue: Any = value
let anyValue2: Any = value2
let unwrappedResult:([Int]?, Bool)
= unwrap(value: anyValue) // ({[0]}, .1 true)
let unwrappedResult2:([Int]?, Bool)
= unwrap(value: anyValue2) // (nil, .1 true)
let unwrappedResult3:([UInt]?, Bool)
= unwrap(value: anyValue) // (nil, .1 false)
let unwrappedResult4:([NSNumber]?, Bool)
= unwrap(value: anyValue) ({[0]}, .1 false)
Ниже приведен код на игровой площадке.
![введите описание изображения здесь]()
Ответ 7
Незначительное изменение на @thm, чтобы полностью развернуть:
func unwrap<T>(_ any: T) -> Any {
let mirror = Mirror(reflecting: any)
guard mirror.displayStyle == .optional, let first = mirror.children.first else {
return any
}
return unwrap(first.value)
}
Ответ 8
Не делая этого слишком сложным, почему бы и нет:
let int:Int? = 1
let str:String? = "foo"
let values:[Any?] = [int,2,str,"bar"]
for var i:Int = 0; i < values.count; i++
{
println("\(values[i]!)")
}
Отпечатки:
1
2
Foo
бар
Ответ 9
Основываясь на решении @bubuxu, можно также:
func unwrap<T: Any>(any: T) -> T? {
let mirror = Mirror(reflecting: any)
guard mirror.displayStyle == .optional else { return any }
guard let child = mirror.children.first else { return nil }
return unwrap(any: child.value) as? T
}
Но при использовании unwrap
вам нужно проверить nil, используя ?? nil
, как это сделано в foo
func foo<T>(_ maybeValue: T?) {
if let value: T = unwrap(any: maybeValue) ?? nil {
print(value)
}
}
Тем не менее, хотя!
(Кто-нибудь получил решение для проверки ?? nil
?)
Ответ 10
В соответствии с использованием шаблонов событий перечисления в Swift 2.0
они могут выглядеть следующим образом:
let pattern :[Int?] = [nil, 332, 232,nil,55]
for case let number? in pattern {
print(number)
}
Вывод:
332,
232,
55