简体   繁体   English

快速将影响值转换为inout通用变量

[英]swift affect value to inout generic variable

I want to simplify this piece of code with a T variable but could not succeed in compiling it. 我想用T变量简化这段代码,但无法成功编译它。 Hope could you give me the way. 希望你能给我办法。

here is the "duplicate" code I want to rewrite : 这是我要重写的“重复”代码:

    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
        }
    }

Here is the code I wrote but do not compile : 这是我编写但未编译的代码:

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
    }
}

for those affectations I got those compilation errors : readValue = integerValue -> 'Int' is not convertible to 'T' readValue = stringValue -> 'String' is not convertible to 'T' 对于那些影响,我得到了那些编译错误:readValue = integerValue->'Int'不可转换为'T'readValue = stringValue->'String'不可转换为'T'

Is there a way to synthetise my two functions with a unique one using generics ? 有没有一种方法可以使用泛型将我的两个函数与唯一的函数合成?

You theoretically could make it compile by adding forced casts, since you already know that value has the type T : 从理论上讲,您可以通过添加强制转换来使其编译,因为您已经知道value具有类型T

    case let integerValue as Int:
        readValue = integerValue as! T
    case let stringValue as String:
        readValue = stringValue as! T

But the far better solution is to use a conditional cast ( as? T ) and conditional binding ( if let ): 但是更好的解决方案是使用条件转换( 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
    }
}

which then works for arbitrary types, not only Int and String . 然后适用于任意类型,不仅限于IntString

A “swiftier” way would be return an optional value (with nil indicating "no value"). “ swiftier”方式将返回一个可选值( nil表示“无值”)。 The code can then be simplified to 然后可以将代码简化为

func getValue<T>(listValues: [Any], numValueToRead: Int) -> T? {
    guard listValues.indices.contains(numValueToRead) else {
        return nil
    }
    return listValues[numValueToRead] as? T
}

This should work: 这应该工作:

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
    }
}

At first sight, the function is wrongly named. 乍一看,该函数的名称错误。 You cannot call function getValue when it returns bool... I would call it transform or modify or something other than get value, because you are NOT getting value. 当它返回布尔值时,您不能调用函数getValue ...我将其称为变换或修改或获取值以外的其他东西,因为您没有获取值。

I think this method suits better your needs, not tested tought it should work. 我认为此方法更适合您的需求,而不是经过测试的方法应该可以工作。

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: Returning optional generic type T is much safer. 说明:返回可选的泛型T更安全。 You try to cast it, you fail and you throw error that something went wrong. 您尝试投射它,失败了,并且抛出了出错的错误消息。 In my opinion saving force casts with throwing errors is much more safer approach, you know what went wrong and so. 在我看来,省力投掷错误是一种更安全的方法,您知道出了什么问题等等。

As @MartinR pointed out, returning a nil value instead of an inout + Bool combination gives the same results, but with less, and more readable code. 正如@MartinR所指出的那样,返回一个nil值而不是inout + Bool组合可以得到相同的结果,但是所用的代码更少且可读性更高。 This is the path Swift also took when importing most of the NSError ** methods from Objective-C (ie dropped the last parameter, imported them as throwable functions). 这是Swift从Objective-C导入大多数NSError **方法时也采用的路径(即删除最后一个参数,将其作为可抛出函数导入)。

These being said, another approach would be to add an extension over Array for extracting the value: 这就是说,另一种方法是在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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM