用于验证数值的Swift通用“数字”协议

时间:2016-12-17 05:27:24

标签: swift protocols

斯威夫特3.

最终我的函数需要接收UInt8数据类型,但我不确定我将从调用者那里收到的参数是IntInt64UIntFloat等。我知道它们将是数字类型,我只是不知道哪种风格。

我能做到:

func foo(value: Int) { }
func foo(value: Float) {}
func foo(value: UInt) {}

但那太疯狂了。所以我认为我可以做一些像创建协议

protocol ValidEncodable {
}

然后传入符合的类型:

func foo(value: ValidEncodable) { }

然后在该函数中,我可以将我的值设置为正确的格式

func foo(value: ValidEncoable) -> UInt8 {
    let correctedValue = min(max(floor(value), 0), 100)
    return UInt8(correctedValue)
}

我真的很想弄清楚

1)如何创建包含所有数字类型的ValidEncodable协议

2)当我得到的值是一个Int而不迭代每个可能的数字类型(即(floor(x)仅在浮点类型上可用)时,如何做floor(value)之类的事情

最终我需要的值是0到100的UInt8。这种疯狂的全部原因是我正在将XML文件解析为我自己的内部数据结构,并且我想对我的值进行一些验证。

2 个答案:

答案 0 :(得分:0)

如果您知道,您的传入值将位于0 ..< 256,那么您只需构建一个UInt8并将其传递给该函数。

func foo(value: UInt8) -> UInt8 { return value }
let number = arbitraryNumber()
print(foo(UInt8(number)))

如果值太大而无法放入字节,这将抛出运行时异常,否则将起作用。您可以通过在第二行和第三行之间进行一些边界检查来防止此类错误。

答案 1 :(得分:0)

这可以在没有协议的情况下完成,并且可以使用编译器检查,这可以大大减少错误的变化。

我的建议是使用部分函数 - 即一个不是取int的函数,它需要一个已经验证的值。请查看this article以获得更深入的部分功能优异原因说明。

你可以构建一个Int0to100结构,它有一个可用的或可抛出的初始化器(取决于品味):

struct Int0to100 {
    let value: UInt8

    init?(_ anInt: Int) {
        guard anInt >= 0 && anInt <= 100 else { return nil }
        value = UInt8(anInt)
    }

    init?(_ aFloat: Float) {
        let flooredValue = floor(aFloat)
        guard flooredValue >= 0 && flooredValue <= 100 else { return nil }
        value = UInt8(flooredValue)
    }

    // ... another initializers can be added the same way
}

并更改foo以允许使用此参数调用:

func foo(value: Int0to100) {
    // do your stuff here, you know for sure, at compile time,
    // that the function can be called with a valid value only
}

您向调用者调用验证整数值的责任,但是验证解析为检查可选项,这很容易,并且允许您以最小的努力处理无效数字的场景。

另一个重要方面是您明确声明了foo函数的域,这改进了代码的整体设计。

而不是最后,在编译时设置的强制执行会大大降低运行时问题的可能性。