有没有办法一起使用模板,输入输出参数和可选参数?

时间:2016-01-15 20:54:21

标签: swift templates pass-by-reference optional-parameters

前几天我遇到了一些有趣的行为。基本上我最初编写了一个辅助函数来防止错误,同时自动对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

但是如果我需要它,那么我将失去我抛出的错误功能。

有没有人有任何想法如何确保我的模板类型在值仍为零时正确传递?我甚至在另一个线程中读到我可能必须使用某种默认值闭包,但看看是否有办法解决这个问题会很有趣。

1 个答案:

答案 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' */