Swift - 将Nil作为带有可选参数的泛型函数的参数

时间:2016-03-16 15:01:54

标签: swift function generics null function-declaration

我正在尝试创建一个可以采用可选参数的泛型函数。 这是我到目前为止所做的:

func somethingGeneric<T>(input: T?) {
    if (input != nil) {
        print(input!);
    }
}

somethingGeneric("Hello, World!") // Hello, World!
somethingGeneric(nil) // Errors!

如图所示,它适用于String,但不适用于nil。 将其与nil一起使用会产生以下两个错误:

error: cannot invoke 'somethingGeneric' with an argument list of type '(_?)'
note: expected an argument list of type '(T?)'

我做错了什么以及如何正确声明/使用此功能?另外,我希望尽可能简化函数的使用(我不想做nil as String?之类的事情。)

6 个答案:

答案 0 :(得分:5)

我想编译器无法弄清T来自nil的内容。

以下工作正常,例如:

somethingGeneric(Optional<String>.None)

答案 1 :(得分:2)

我认为你永远不会调用somethingGeneric(nil),但主要是somethingGeneric(value)somethingGeneric(function()),编译器有足够的信息不会被试图猜测类型:

func somethingGeneric<T>(input: T?) {
    if let input = input {
        print(input);
    }
}

func neverString() -> String? {
    return nil
}

let a: String? = nil

somethingGeneric("Hello, World!") // Hello, World!
somethingGeneric(a) // Nothing and no error
somethingGeneric(neverString()) // Nothing and no error

另外,我会使用if let语法而不是if(value != nil)

答案 2 :(得分:1)

我明白了:

func somethingGeneric<T>(input: T?) {
    if (input != nil) {
        print(input!);
    }
}

func somethingGeneric(input: NilLiteralConvertible?) {}


somethingGeneric("Hello, World!") // Hello, World!
somethingGeneric(nil) // *nothing printed*
somethingGeneric(nil as String?) // *nothing printed*

答案 3 :(得分:1)

我相信你通过要求能够传递无类型nil(实际上并不存在;甚至nil有一个类型)来使问题过于复杂。虽然您的答案中的方法似乎有效,但由于可选促销,它允许创建??类型。你经常会很幸运,但是我看到它以令人沮丧的方式爆炸,并且调用了错误的功能。问题是String可以隐式提升为String?String?可以隐式提升为String??。当隐私地显示??时,几乎总会出现混淆。

正如MartinR指出的那样,您的方法对于调用哪个版本并不是非常直观。 UnsafePointer也是NilLiteralConvertible。因此,推断将调用哪个函数是很棘手的。 “棘手的理由”使它成为令人困惑的错误的可能来源。

您的问题存在的唯一时间是您传递文字nil。正如@Valentin指出的那样,如果您传递的变量恰好是 nil,那么就没有问题;你不需要特殊情况。为什么强制调用者传递无类型的nil?让呼叫者什么都不通过。

我假设somethingGeneric在传递nil的情况下做了一些真正有趣的事情。如果不是这样的话;如果您显示的代码表示实际功能(即所有内容都包含在if (input != nil)检查中),那么这不是问题。只是不要打电话给somethingGeneric(nil);这是一个可证明的无操作。只需删除代码行即可。但我会假设有一些“其他工作。”

func somethingGeneric<T>(input: T?) {
    somethingGeneric() // Call the base form
    if (input != nil) {
        print(input!);
    }
}

func somethingGeneric() {
   // Things you do either way
}

somethingGeneric("Hello, World!") // Hello, World!
somethingGeneric() // Nothing

答案 4 :(得分:1)

好问答。我有一个Swift 4更新贡献:

var str: String? = "Hello, playground"
var list: Array<String>? = ["Hello", "Coder256"]

func somethingGeneric<T>(_ input: T?) {
  if (input != nil) {
    print(input!);
  }
}

func somethingGeneric(_ input: ExpressibleByNilLiteral?) {}


somethingGeneric("Hello, World!")    // Hello, World!
somethingGeneric(nil)                // *nothing printed*
somethingGeneric(nil as String?)     // *nothing printed*
somethingGeneric(str)                // Hello, playground
str = nil
somethingGeneric(str)                // *nothing printed*
somethingGeneric(list)               // ["Hello", "Coder256"]
list = nil
somethingGeneric(list)               // *nothing printed*

答案 5 :(得分:0)

这是我想出的在Swift 5上编译的解决方案,因为这里的许多解决方案都不是我编译的。当我使用存储的变量来帮助解决问题时,它可能被认为是hacky。我无法提供Swift 5版本的nil参数来解析为T的类型。

class MyClass {
    func somethingGeneric<T>(input: T?) {
        if let input = input {
            print(input)
        }
    }

    func somethingGeneric() {
        somethingGeneric(Object.Nil)
    }

}

final class Object {
    static var Nil: Object? //this should never be set
}