Swift - 在带有可选参数的泛型函数中将 Nil 作为参数
Swift - Take Nil as Argument in Generic Function with Optional Argument
我正在尝试创建一个可以接受可选参数的通用函数。
这是我目前所拥有的:
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?)'
我做错了什么,我应该如何正确declare/use这个功能?另外,我想尽可能简单地使用函数(我不想做类似 nil as String?
)的事情。
我猜编译器无法从 nil
.
中找出 T
是什么
以下工作正常,例如:
somethingGeneric(Optional<String>.None)
我想通了:
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(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)
。
我认为您要求能够传递未类型化的 nil
(实际上并不存在;甚至 nil
也有类型),从而使问题过于复杂。虽然您的答案中的方法似乎有效,但由于 Optional 提升,它允许创建 ??
类型。你经常会很幸运并且成功了,但我已经看到它以非常令人沮丧的方式爆炸并且调用了错误的函数。问题是 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
很好的问答。我有一个 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*
这是我想出的在 Swift 5 上编译的解决方案,因为这里的许多解决方案都没有为我编译。它可能被认为是 hacky,因为我使用存储变量来帮助事情进展。我无法想出解析为类型 T
.
的 nil 参数的 Swift 5 版本
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
}
实际上有一种方法可以做到这一点,灵感来自 Alamofire 的内部代码。
您无需安装 Alamofire 即可使用此解决方案。
用法
你有问题的方法定义
func someMethod<SomeGenericOptionalCodableType: Codable>(with someParam: SomeGenericOptionalCodableType? = nil) {
// your awesome code goes here
}
有效的方法 ✅
// invoke `someMethod` correctly
let someGoodParam1 = Alamofire.Empty.value
someMethod(with: someGoodParam1)
我认为可以在 someMethod
定义中使用 Alamofire.Empty.value
作为默认值作为参数。
什么不起作用 ❌
// invoke `someMethod` incorrectly
let someBadParam1: Codable? = nil
let someBadParam2 = nil
someMethod(with: someBadParam1)
someMethod(with: someBadParam2)
解决方案定义(source)
/// Type representing an empty value. Use `Empty.value` to get the static instance.
public struct Empty: Codable {
/// Static `Empty` instance used for all `Empty` responses.
public static let value = Empty()
}
我正在尝试创建一个可以接受可选参数的通用函数。 这是我目前所拥有的:
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?)'
我做错了什么,我应该如何正确declare/use这个功能?另外,我想尽可能简单地使用函数(我不想做类似 nil as String?
)的事情。
我猜编译器无法从 nil
.
T
是什么
以下工作正常,例如:
somethingGeneric(Optional<String>.None)
我想通了:
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(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)
。
我认为您要求能够传递未类型化的 nil
(实际上并不存在;甚至 nil
也有类型),从而使问题过于复杂。虽然您的答案中的方法似乎有效,但由于 Optional 提升,它允许创建 ??
类型。你经常会很幸运并且成功了,但我已经看到它以非常令人沮丧的方式爆炸并且调用了错误的函数。问题是 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
很好的问答。我有一个 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*
这是我想出的在 Swift 5 上编译的解决方案,因为这里的许多解决方案都没有为我编译。它可能被认为是 hacky,因为我使用存储变量来帮助事情进展。我无法想出解析为类型 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
}
实际上有一种方法可以做到这一点,灵感来自 Alamofire 的内部代码。
您无需安装 Alamofire 即可使用此解决方案。
用法
你有问题的方法定义
func someMethod<SomeGenericOptionalCodableType: Codable>(with someParam: SomeGenericOptionalCodableType? = nil) {
// your awesome code goes here
}
有效的方法 ✅
// invoke `someMethod` correctly
let someGoodParam1 = Alamofire.Empty.value
someMethod(with: someGoodParam1)
我认为可以在 someMethod
定义中使用 Alamofire.Empty.value
作为默认值作为参数。
什么不起作用 ❌
// invoke `someMethod` incorrectly
let someBadParam1: Codable? = nil
let someBadParam2 = nil
someMethod(with: someBadParam1)
someMethod(with: someBadParam2)
解决方案定义(source)
/// Type representing an empty value. Use `Empty.value` to get the static instance.
public struct Empty: Codable {
/// Static `Empty` instance used for all `Empty` responses.
public static let value = Empty()
}