Swift 具有特定自身类型的协议扩展
Swift protocol extension with specific Self type
我为 UnsignedInteger 协议添加了一个扩展,以添加一个以十六进制格式表示数字的十六进制方法。我还希望特定的符合结构具有参数的默认值。下面是我写的。
extension UnsignedInteger {
func hex(withFieldWidth fieldWidth: Int, andUseUppercase uppercase: Bool = true) -> String {
return String(format: "%0\(fieldWidth)\(uppercase ? "X" : "x")", self as! CVarArg)
}
}
extension UnsignedInteger where Self == UInt8 {
func hex(withFieldWidth fieldWidth: Int = 2, andUseUppercase uppercase: Bool = true) -> String {
// should call the UnsignedInteger implementation with the default parameters
return hex(withFieldWidth: fieldWidth, andUseUppercase: uppercase)
}
}
extension UnsignedInteger where Self == UInt16 {
func hex(withFieldWidth fieldWidth: Int = 4, andUseUppercase uppercase: Bool = true) -> String {
// should call the UnsignedInteger implementation with the default parameters
return hex(withFieldWidth: fieldWidth, andUseUppercase: uppercase)
}
}
但是,对于 UInt8 和 UInt16 特定的扩展,它似乎是在调用自身而不是第一个扩展块的十六进制,正如我为 UInt8 和 UInt16 块收到的警告消息所解释的那样:All paths through this function will call itself
.
如果我从 UInt8 和 UInt16 块中删除 fieldWidh
,调用 hex(使用 fieldWidth
的硬编码值)似乎可以正常编译,我相信这种方式是从调用 hex 方法第一个扩展块。下面是编译好的代码。
extension UnsignedInteger {
func hex(withFieldWidth fieldWidth: Int, andUseUppercase uppercase: Bool = true) -> String {
return String(format: "%0\(fieldWidth)\(uppercase ? "X" : "x")", self as! CVarArg)
}
}
extension UnsignedInteger where Self == UInt8 {
func hex(andUseUppercase uppercase: Bool = true) -> String {
// should call the UnsignedInteger implementation with the default parameters
return hex(withFieldWidth: 2, andUseUppercase: uppercase)
}
}
extension UnsignedInteger where Self == UInt16 {
func hex(andUseUppercase uppercase: Bool = true) -> String {
// should call the UnsignedInteger implementation with the default parameters
return hex(withFieldWidth: 4, andUseUppercase: uppercase)
}
}
有没有办法在进行协议扩展时为特定的符合结构指定参数的默认值?
有没有办法在进行协议扩展时为特定的符合结构指定参数的默认值?
You have highlighted the problems with this approach in the question already.
如何换一种方式解决?
UnsignedInteger
inherits from BinaryInteger
that can provide you bitWidth
information (UInt8
=> 8
, UInt16
=> 16
and so on).
extension UnsignedInteger {
func hex(uppercase: Bool = true) -> String {
let fieldWidth = self.bitWidth / 4
return String(format: "%0\(fieldWidth)\(uppercase ? "X" : "x")", self as! CVarArg)
}
}
以上使其适用于 UInt
、UInt8
、UInt16
、UInt32
和 UInt64
。
更进一步,您可以使用 FixedWidthInteger
执行此操作,现在它适用于所有有符号和无符号整数。
这已经得到解答,但可以改进。无需强制转换为 CVarArg,您可以简单地添加一个约束 where Self: CVarArg。您还可以将字符数作为字符串初始值设定项的 CVarArg 传递,并在字符串格式中使用 *
。
extension UnsignedInteger where Self: CVarArg {
func hex(uppercase: Bool = true) -> String {
.init(format: "%0*\(uppercase ? "X" : "x")", bitWidth / 4, self)
}
}
UInt8(255).hex() // "FF"
UInt8(255).hex(uppercase: false) // "ff"
UInt16(255).hex() // "00FF"
UInt16(255).hex(uppercase: false) // "00ff"
UInt32(255).hex() // "000000FF"
UInt32(255).hex(uppercase: false) // "000000ff"
我为 UnsignedInteger 协议添加了一个扩展,以添加一个以十六进制格式表示数字的十六进制方法。我还希望特定的符合结构具有参数的默认值。下面是我写的。
extension UnsignedInteger {
func hex(withFieldWidth fieldWidth: Int, andUseUppercase uppercase: Bool = true) -> String {
return String(format: "%0\(fieldWidth)\(uppercase ? "X" : "x")", self as! CVarArg)
}
}
extension UnsignedInteger where Self == UInt8 {
func hex(withFieldWidth fieldWidth: Int = 2, andUseUppercase uppercase: Bool = true) -> String {
// should call the UnsignedInteger implementation with the default parameters
return hex(withFieldWidth: fieldWidth, andUseUppercase: uppercase)
}
}
extension UnsignedInteger where Self == UInt16 {
func hex(withFieldWidth fieldWidth: Int = 4, andUseUppercase uppercase: Bool = true) -> String {
// should call the UnsignedInteger implementation with the default parameters
return hex(withFieldWidth: fieldWidth, andUseUppercase: uppercase)
}
}
但是,对于 UInt8 和 UInt16 特定的扩展,它似乎是在调用自身而不是第一个扩展块的十六进制,正如我为 UInt8 和 UInt16 块收到的警告消息所解释的那样:All paths through this function will call itself
.
如果我从 UInt8 和 UInt16 块中删除 fieldWidh
,调用 hex(使用 fieldWidth
的硬编码值)似乎可以正常编译,我相信这种方式是从调用 hex 方法第一个扩展块。下面是编译好的代码。
extension UnsignedInteger {
func hex(withFieldWidth fieldWidth: Int, andUseUppercase uppercase: Bool = true) -> String {
return String(format: "%0\(fieldWidth)\(uppercase ? "X" : "x")", self as! CVarArg)
}
}
extension UnsignedInteger where Self == UInt8 {
func hex(andUseUppercase uppercase: Bool = true) -> String {
// should call the UnsignedInteger implementation with the default parameters
return hex(withFieldWidth: 2, andUseUppercase: uppercase)
}
}
extension UnsignedInteger where Self == UInt16 {
func hex(andUseUppercase uppercase: Bool = true) -> String {
// should call the UnsignedInteger implementation with the default parameters
return hex(withFieldWidth: 4, andUseUppercase: uppercase)
}
}
有没有办法在进行协议扩展时为特定的符合结构指定参数的默认值?
有没有办法在进行协议扩展时为特定的符合结构指定参数的默认值?
You have highlighted the problems with this approach in the question already.
如何换一种方式解决?
UnsignedInteger
inherits fromBinaryInteger
that can provide youbitWidth
information (UInt8
=>8
,UInt16
=>16
and so on).
extension UnsignedInteger {
func hex(uppercase: Bool = true) -> String {
let fieldWidth = self.bitWidth / 4
return String(format: "%0\(fieldWidth)\(uppercase ? "X" : "x")", self as! CVarArg)
}
}
以上使其适用于 UInt
、UInt8
、UInt16
、UInt32
和 UInt64
。
更进一步,您可以使用 FixedWidthInteger
执行此操作,现在它适用于所有有符号和无符号整数。
这已经得到解答,但可以改进。无需强制转换为 CVarArg,您可以简单地添加一个约束 where Self: CVarArg。您还可以将字符数作为字符串初始值设定项的 CVarArg 传递,并在字符串格式中使用 *
。
extension UnsignedInteger where Self: CVarArg {
func hex(uppercase: Bool = true) -> String {
.init(format: "%0*\(uppercase ? "X" : "x")", bitWidth / 4, self)
}
}
UInt8(255).hex() // "FF"
UInt8(255).hex(uppercase: false) // "ff"
UInt16(255).hex() // "00FF"
UInt16(255).hex(uppercase: false) // "00ff"
UInt32(255).hex() // "000000FF"
UInt32(255).hex(uppercase: false) // "000000ff"