前几天我遇到了一些有趣的行为。基本上我最初编写了一个辅助函数来防止错误,同时自动对JSON属性进行类型转换。它看起来像这样:
func readData<T>(inout output:T, _ input:AnyObject?, _ throwError:Bool = true) throws
{
if (input == nil) {
if (throwError) {
throw ConvertError.MissingParameter
}
}
else {
if let inputObject:T = input as? T {
output = inputObject
}
else if (throwError) {
throw ConvertError.WrongType
}
}
}
var myProperty:String
try readData(&myProperty, myJson["data"], true)
这会检查属性是否存在,并且它是正确的类型。如果一切顺利, myProperty 中的值会发生变化。
不久之后,我需要做一些改变。我创建了一个名为 properties 的类,它有一个属性列表。这类类有2个变量: originalProperties 和 modifiedProperties 这些类中的每个属性现在都是可选变量,专门用于跟踪用户已更改的属性。基本上它看起来像这样:
class Properties
{
var x:Int?
var y:Int?
}
现在我跑这个:
try readData(&originalProperties.x, myJson["x"], false)
它不再起作用了。我看了another question,它解释了发生了什么。基本上,当 x 仍然具有 nil 的值时(因为它是可选的),我将nil值传递给我的 readData 函数,所以模板类型没有正确设置,这就是输入失败的原因? T 代码。
幸运的是,我不再需要具备这样的创造性功能。我可以用这个:originalProperties.x= obj["x"] as? Int
但是如果我需要它,那么我将失去我抛出的错误功能。
有没有人有任何想法如何确保我的模板类型在值仍为零时正确传递?我甚至在另一个线程中读到我可能必须使用某种默认值闭包,但看看是否有办法解决这个问题会很有趣。
答案 0 :(得分:0)
这里的主要问题是,通用T
永远不会本身知道它是否属于Optional
类型,这使得成功转换为{{1} T
实际上是T
类型的情况下的棘手问题。我们自己可以断言Optional<SomeType>
是一个可选类型(检查T
等),但这仍然无法立即解决转换为Mirror(reflecting: ...).displayStyle == .Optional
的问题。相反,我们可以使用另一种方法,如下所示。
我们可以通过创建两个T
函数来解决此问题,一个函数采用可选的通用readData(...)
参数,类型inout
,另一个函数采用隐式非可选泛型{ {1}}参数U?
(仅在inout
函数无法使用时调用,因此隐式只调用非选项)。反过来,这两个功能是最小的,基本上只调用你的核心&#34; U
功能,我们已调整U?
通用参数现在明确可选,即dataReader(..)
。
inout
在我们尝试这一点时,我们发现现在我们正在寻找我们正在寻找的行为,即使作为T?
参数发送的参数是enum ConvertError: ErrorType {
case MissingParameter
case WrongType
}
/* optional inout parameter */
func readData<U>(inout output: U?, _ input: AnyObject?, _ throwError: Bool = true) throws {
try readDataCore(&output, input, throwError)
}
/* non-optional inout parameter */
func readData<U>(inout output: U, _ input: AnyObject?, _ throwError: Bool = true) throws {
var outputOpt : U? = output
try readDataCore(&outputOpt, input, throwError)
output = outputOpt!
/* you could use a guard-throw here for the unwrapping of 'outputOpt', but
note that 'outputOpt' is initialized with a non-nil value, and that it can
never become 'nil' in readDataHelper; so "safe" forced unwrapping here. */
}
/* "core" function */
func readDataCore<T>(inout output: T?, _ input: AnyObject?, _ throwError: Bool = true) throws
{
if (input == nil) {
if (throwError) {
throw ConvertError.MissingParameter
}
}
else {
if let inputObject: T = input as? T {
output = inputObject
}
else if (throwError) {
throw ConvertError.WrongType
}
}
}
或inout
。
示例1 :使用值为nil
的可选Optional
参数
inout
示例2 :使用可选的nil
参数,但是具有非可选值
class Properties
{
var x:Int?
var y:Int?
}
var myJson : [String:Int] = ["data":10]
var originalProperties = Properties()
do {
try readData(&originalProperties.x, myJson["data"], true)
print("originalProperties.x = \(originalProperties.x ?? 0)")
} catch ConvertError.MissingParameter {
print("Missing parameter")
} catch ConvertError.WrongType {
print("Wrong type")
} catch {
print("Unknown error")
}
/* Prints: 'originalProperties.x = 10', ok! */
// try some non-existing key 'foo'
do {
try readData(&originalProperties.x, myJson["foo"], true)
print("originalProperties.x = \(originalProperties.x ?? 0)")
} catch ConvertError.MissingParameter {
print("Missing parameter")
} catch ConvertError.WrongType {
print("Wrong type")
} catch {
print("Unknown error")
}
/* Prints: 'Missing parameter', ok! */
示例3 :使用非可选的inout
参数
class Properties
{
var x:Int? = 1
var y:Int? = 1
}
var myJson : [String:Int] = ["data":10]
var originalProperties = Properties()
// ...
/* Same results as for Example 1:
'originalProperties.x = 10' and 'Missing parameter' */