быстрый необязательный универсальный тип и вложенная необязательная распаковка - PullRequest
0 голосов
/ 05 июня 2019

У меня есть этот кусок кода

class MyObject<T> {

    func start(_ value: T?) {
        if let value = value {
            doSomething(value)
        }
    }

    func doSomething(_ value: T) {
        print(value)
    }
}

MyObject<String>().start("some")
// prints "some"

MyObject<String?>().start(nil)
// doesn't print anything

Мне нужно вызывать -doSomething: для каждого допустимого значения, переданного в -start:.А когда T == String?, то nil является допустимым значением.

Нужно ли мне писать две версии -start в расширениях MyObject с условиями для типа T?И как это сделать?

Ответы [ 4 ]

2 голосов
/ 05 июня 2019

Вы можете заставить вашу функцию start принимать необязательный T и просто всегда вызывать doSomething вместо того, чтобы пытаться развернуть ее сначала. Это позволит вам вызывать start(nil), только если сам T является необязательным типом:

class MyObject<T> {

    func start(_ value: T) {
        doSomething(value)
    }

    func doSomething(_ value: T) {
        print(value)
    }
}

MyObject<String>().start("some")
// prints "some"

MyObject<String?>().start(nil)
// prints "nil"

Если вы хотите оставить параметр равным start как необязательный, тогда ваш исходный код действительно будет работать, но вам нужно изменить способ передачи значения во втором примере.

Поскольку T равно String?, параметр в вашем методе имеет тип String??, и передача nil String?? отличается от передачи с String?, который содержит nil.

Если вы называете это как:

MyObject<String?>().start(.some(nil))

или

let string: String? = nil
MyObject<String?>().start(string)

Тогда будет напечатано "ноль"

0 голосов
/ 05 июня 2019

Если значение равно нулю, при развертывании значение не будет введено в оператор if. Вместо этого вы можете сделать это:

class MyObject<T> {

    func start(_ value: T?) {
        if let value = value {
            doSomething(value)
        } else {
            print("nil")
        }
    }

    func doSomething(_ value: T) {
        print(value)
    }
}
0 голосов
/ 05 июня 2019

В Swift необязательно просто общее перечисление с двумя падежами:

enum Optional<T> {
    case some(T)
    case none
}

Например, String? - это то же самое, что и Optional<String>.

Если вы объявите MyObject<String?>, в основном вы создаете MyObject<Optional<String>>, то ваш конкретный start метод будет

func start(_ value: Optional<Optional<String>>) { ... }

Это означает, что если вы назовете его как start(nil), весь объект будет, конечно, равен нулю, и if let не удастся. Однако вы можете вызвать эту функцию следующим образом

MyObject<String?>().start(Optional.some(Optional.none))
MyObject<String?>().start(.some(.none)) // -> shorter version with type inference

По сути, теперь существует внешний необязательный параметр, и развертывание работает, но внутренним является nil.

Однако я до сих пор не могу понять, зачем вам делать что-то подобное

0 голосов
/ 05 июня 2019

Поскольку let value = value не работает со вторым входом, вам нужно обрабатывать этот случай отдельно.

func start(_ value: T?) {
    if let value = value {
        doSomething(value)
    }
    else
    {
        print("nil input")
    }
}
...