有没有办法在 Swift 中覆盖数组到字符串的转换?
Is there a way to override Array to String casting in Swift?
我正在研究 Swift 试图让它看起来更“动态输入”——只是为了好玩,没有预期的生产价值。
现在我坚持将内置类型转换为 String
的覆盖行为。
例如,我希望看到 Array
的输出:
let nums = [1, 2, 3]
print(nums) // "I'm an array"
到目前为止我已经尝试过
- 扩展
NSArray
(不编译)
- 实施
CustomStringConvertible
(不编译)
- 对
Array
进行扩展(编译,不做任何更改)
看来我走错了路:
extension Array: CustomStringConvertible {
public var description: String { return "An array" }
}
给出警告:
Conformance of 'Array' to protocol 'CustomStringConvertible' was already stated in the type's module 'Swift'
这在 Swift 中可行吗?
这不起作用,因为数组覆盖了描述。如果数组没有覆盖它,那么它将打印 "An array"。 class 方法 'wins' 在扩展上。
extension Array {
public var description: String { return "An array" }
}
您可以为您的阵列创建一个包装器 class。这是一种解决方法,但不会覆盖数组本身的描述。
class ArrayWrapper<T> : CustomStringConvertible{
var array : Array<T> = Array<T>()
var description: String { return "An array" }
}
然后你可以像这样使用它。
var array = ArrayWrapper<Int>()
array.array = [1,2,3]
print(array) //prints "An Array"
print(array.array) //still prints "[1, 2, 3]"
似乎既不能子类化也不能覆盖内置 Array
类型。
虽然我们可以使用包装器(归功于@Yannick)并实现 ArrayLiteralConvertible
协议,因此我们可以使用方括号进行初始化。
struct Array<T> {
let array: [T]
}
extension Array: ArrayLiteralConvertible {
init(arrayLiteral elements: T...) {
self.array = elements
}
}
extension Array: CustomStringConvertible {
var description: String { return "An array" }
}
let array = [1,2,3]
print(array) // "An array\n"
Swift 不允许您为已经声明该协议的类型提供协议覆盖:
Imagine that you have a set of integers and you want to override the default implementation of CustomStringConvertible for a Set
对所有这一切最简单的回答是:你不能,这是一个功能而不是错误。 Set 不是您的类型,您不能像这样更改其已定义的行为。
拦截和改变类型的行为以执行与该类型最初设计的不同的事情的能力是强大的,但充满了缺点。在这种情况下,您选择了一个相当良性的东西来改变,但在其他情况下,这样做可能会通过改变其程序范围的行为来对类型的假定不变量造成各种损害。
更改类型以执行与此不同的操作的最佳方法是将其包装在您自己的低成本结构中。不幸的是,这确实意味着要为转发编写大量样板文件——尽管随着我们获得合成一致性和动态成员查找等功能,现在越来越少了。希望有一天我们会获得一些功能,这些功能确实可以更轻松地创建具有自定义行为的新类型。
我正在研究 Swift 试图让它看起来更“动态输入”——只是为了好玩,没有预期的生产价值。
现在我坚持将内置类型转换为 String
的覆盖行为。
例如,我希望看到 Array
的输出:
let nums = [1, 2, 3]
print(nums) // "I'm an array"
到目前为止我已经尝试过
- 扩展
NSArray
(不编译) - 实施
CustomStringConvertible
(不编译) - 对
Array
进行扩展(编译,不做任何更改)
看来我走错了路:
extension Array: CustomStringConvertible {
public var description: String { return "An array" }
}
给出警告:
Conformance of 'Array' to protocol 'CustomStringConvertible' was already stated in the type's module 'Swift'
这在 Swift 中可行吗?
这不起作用,因为数组覆盖了描述。如果数组没有覆盖它,那么它将打印 "An array"。 class 方法 'wins' 在扩展上。
extension Array {
public var description: String { return "An array" }
}
您可以为您的阵列创建一个包装器 class。这是一种解决方法,但不会覆盖数组本身的描述。
class ArrayWrapper<T> : CustomStringConvertible{
var array : Array<T> = Array<T>()
var description: String { return "An array" }
}
然后你可以像这样使用它。
var array = ArrayWrapper<Int>()
array.array = [1,2,3]
print(array) //prints "An Array"
print(array.array) //still prints "[1, 2, 3]"
似乎既不能子类化也不能覆盖内置 Array
类型。
虽然我们可以使用包装器(归功于@Yannick)并实现 ArrayLiteralConvertible
协议,因此我们可以使用方括号进行初始化。
struct Array<T> {
let array: [T]
}
extension Array: ArrayLiteralConvertible {
init(arrayLiteral elements: T...) {
self.array = elements
}
}
extension Array: CustomStringConvertible {
var description: String { return "An array" }
}
let array = [1,2,3]
print(array) // "An array\n"
Swift 不允许您为已经声明该协议的类型提供协议覆盖:
Imagine that you have a set of integers and you want to override the default implementation of CustomStringConvertible for a Set
对所有这一切最简单的回答是:你不能,这是一个功能而不是错误。 Set 不是您的类型,您不能像这样更改其已定义的行为。
拦截和改变类型的行为以执行与该类型最初设计的不同的事情的能力是强大的,但充满了缺点。在这种情况下,您选择了一个相当良性的东西来改变,但在其他情况下,这样做可能会通过改变其程序范围的行为来对类型的假定不变量造成各种损害。
更改类型以执行与此不同的操作的最佳方法是将其包装在您自己的低成本结构中。不幸的是,这确实意味着要为转发编写大量样板文件——尽管随着我们获得合成一致性和动态成员查找等功能,现在越来越少了。希望有一天我们会获得一些功能,这些功能确实可以更轻松地创建具有自定义行为的新类型。