Swift - 在具有可选参数的泛型函数中以 Nil 作为参数

Posted

技术标签:

【中文标题】Swift - 在具有可选参数的泛型函数中以 Nil 作为参数【英文标题】:Swift - Take Nil as Argument in Generic Function with Optional Argument 【发布时间】:2016-07-02 13:16:59 【问题描述】:

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

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?之类的事情)。

【问题讨论】:

【参考方案1】:

我猜编译器无法从 nil 中找出 T 是什么。

例如,以下工作很好:

somethingGeneric(Optional&lt;String&gt;.None)

【讨论】:

OP提到的nil as String?基本上也是这样做的 @WillM。我还提到我想要这种行为。 我认为没有办法解决它。也没有func somethingGenericWithNil() 而是调用它:)【参考方案2】:

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

正如 MartinR 所指出的,您的方法对于调用哪个版本不是很直观。 UnsafePointer 也是 NilLiteralConvertible。所以很难推断将调用哪个函数。 “难以推理”使其成为令人困惑的错误的可能来源。

您的问题存在的唯一时间是当您传递文字 nil 时。正如@Valentin 所指出的,如果您传递一个恰好是 be 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(input: "Hello, World!") // Hello, World!
somethingGeneric() // Nothing

【讨论】:

Rob 的出色回应,评论因为这绝对应该是公认的答案 你说得对,@RobNapier - 我想我在测试时搞砸了复制和粘贴。我只是要删除原来的评论。【参考方案3】:

好问题和答案。我有一个 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*

【讨论】:

【参考方案4】:

我想通了:

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*

【讨论】:

您的 somethingGeneric&lt;T&gt;(input:T?) func 仍然不接受 nil 作为值。它只是通过空函数并且不打印任何内容,因为该函数不执行任何操作。有了这个,如果你用nil调用func,它会通过以input: NilLiteralConvertible为参数的func,但是如果你用nil Optional调用它,比如说let x: String?,它会通过func以input: T? 作为参数,因此您将不得不复制您的零处理逻辑。 请注意,例如,let ptr : UnsafePointer&lt;Int&gt;? = ... ; somethingGeneric(ptr) 会调用 second 函数,因为 UnsafePointerNilLiteralConvertible,而第二个通用函数更具体。 @MartinR 所有可选项都是 NilLiteralConvertible,但可选字符串仍然会转到第一个函数,无论它是 Optional.Some 还是 Optional.None @WillM.:第二个函数采用NilLiteralConvertible?,而String? 与该类型不匹配(因为String 不是NilLiteralConvertible)。 @WillM。这是可选的促销。在您描述的情况下,编译器正在升级为与NilLiteralConvertible? 匹配的String??。我的经验是,这种对泛型的使用是非常脆弱的,可选的提升会以令人惊讶的方式烧毁你,创建?? 类型。我建议以某种方式限制T【参考方案5】:

我认为您永远不会调用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)

【讨论】:

【参考方案6】:

这是我想出的在 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

【讨论】:

以上是关于Swift - 在具有可选参数的泛型函数中以 Nil 作为参数的主要内容,如果未能解决你的问题,请参考以下文章

从 C++ 中的泛型函数返回可选的

泛型类的可选方法参数的类型

返回类型为协议的泛型函数与参数和返回类型为协议的非泛型函数的区别

Swift 中的泛型 - “无法推断出泛型参数‘T’

TypeScript 中具有泛型类型参数的泛型类型的替代方案

Swift2.0(17)泛型技术