我想用T变量简化这段代码但是无法成功编译它。希望你能给我路。
这是我要重写的“重复”代码:
func getIntegerValue (listValues: [Any], numValueToRead: Int, readValue: inout Int) -> Bool {
if numValueToRead < 0 || numValueToRead >= listValues.count {
return false
}
let value = listValues [numValueToRead]
if type (of: value) == type(of: readValue) {
readValue = value as! Int
return true
} else {
return false
}
}
func getStringValue (listValues: [Any], numValueToRead: Int, readValue: inout String) -> Bool {
if numValueToRead < 0 || numValueToRead >= listValues.count {
return false
}
let value = listValues [numValueToRead]
if type (of: value) == type(of: readValue) {
readValue = value as! String
return true
} else {
return false
}
}
这是我写的代码,但不编译:
func getValue <T> (listValues: [Any], numValueToRead: Int, readValue: inout T) -> Bool {
if numValueToRead < 0 || numValueToRead >= listValues.count {
return false
}
let value = listValues [numValueToRead]
if type (of: value) == type(of: readValue) {
switch value {
case let integerValue as Int:
readValue = integerValue
case let stringValue as String:
readValue = stringValue
default:
return false
}
return true
} else {
return false
}
}
对于那些影响,我得到了那些编译错误:readValue = integerValue - >'Int'不能转换为'T'readValue = stringValue - >'String'不能转换为'T'
有没有办法用一个使用泛型的独特函数来合成我的两个函数?
你理论上可以通过添加强制转换来编译它,因为你已经知道value
的类型为T
:
case let integerValue as Int:
readValue = integerValue as! T
case let stringValue as String:
readValue = stringValue as! T
但更好的解决方案是使用条件转换(as? T
)和条件绑定(if let
):
func getValue<T>(listValues: [Any], numValueToRead: Int, readValue: inout T) -> Bool {
if numValueToRead < 0 || numValueToRead >= listValues.count {
return false
}
let value = listValues[numValueToRead]
if let tvalue = value as? T {
readValue = tvalue
return true
} else {
return false
}
}
然后适用于任意类型,不仅Int
和String
。
“更快”的方式将返回一个可选值(nil
表示“无值”)。然后可以将代码简化为
func getValue<T>(listValues: [Any], numValueToRead: Int) -> T? {
guard listValues.indices.contains(numValueToRead) else {
return nil
}
return listValues[numValueToRead] as? T
}
这应该工作:
func getValue <T> (listValues: [Any], numValueToRead: Int, readValue: inout T) -> Bool {
if numValueToRead < 0 || numValueToRead >= listValues.count {
return false
}
let value = listValues [numValueToRead]
if type (of: value) == type(of: readValue) {
if let genericValue = value as? T {
readValue = genericValue
return true
}
return false
} else {
return false
}
}
乍一看,该功能被错误地命名。当它返回bool时你不能调用函数getValue ...我会称之为变换或修改或者除了获取值之外的东西,因为你没有获得价值。
我认为这种方法更适合您的需求,而不是经过测试认为它应该有效。
func transformValue<T>(from listValues: [Any], numValueToRead: Int, readValue: inout T?) throws -> Bool {
// Guard suits better this case...
guard numValueToRead > 0 || numValueToRead < listValues.count else { return false }
let value = listValues[numValueToRead]
if type (of: value) == type(of: readValue) {
guard let value = value as? T else {
throw NSError(
domain: "Smth",
code: 1,
userInfo: ["Description": "Failed to cast to generic type T"]
)
}
readValue = value as? T
return true
}
return false // No need to call else...
}
Explenation:返回可选的泛型类型T更安全。你试图施放它,你失败了,你会抛出错误的东西。在我看来,投掷错误的拯救力量更加安全,你知道出了什么问题。
正如@MartinR指出的那样,返回nil
值而不是inout
+ Bool
组合会得到相同的结果,但代码更少,更易读。这是Swift在从Objective-C导入大多数NSError **
方法时所采用的路径(即删除了最后一个参数,将它们作为可抛出函数导入)。
话虽如此,另一种方法是在Array
上添加一个扩展来提取值:
extension Array {
subscript<T>(_ index: Int, as type: T.Type) -> T? {
guard 0..<count ~= index else { return nil }
return self[index] as? T
}
}
let arr: [Any] = [1, "two", 3, "four"]
arr[1, as: String.self] // two
arr[2, as: String.self] // nil