Return 使用时来自结构的值
Return value from struct when used
我是 Swift 的新手,正在学习 struct
。但是,我有一个听起来很明显的问题,但我不确定该怎么做。我正在操场上做这一切。
我有一个名为 Colour
的 struct
,我在其中创建了 RGB 颜色类型。我想访问它的变量(例如,通过执行 yellow.red
这将读取和写入变量以找到黄色中红色的值)。
struct Colour {
var red: Int
var blue: Int
var green: Int
var rgb: [Int] // <- I don't want to have this variable, I want to make the struct Colour be this array
init(red: Int, green: Int, blue: Int) {
self.red = red
self.green = green
self.blue = blue
rgb = [red, green, blue]
}
}
致电:
let red = Colour(red: 255, green: 0, blue: 0) // Colour
red.red // 255
red.green // 0
red.blue // 0
red // Colour
red.rgb // [255, 0, 0]
当我访问 red
时,我希望它自动 return red.rgb
的值,而不需要变量。
那么我如何调用 red
和 return 一个值为 [255, 0, 0]
的 Array
?
备注:
无法实现get
或set
我不能使用Colour
作为变量,因为我需要初始化
无法使用 return
关键字,因为 struct
不能以这种方式工作
编辑
抱歉没有说清楚。我一直在寻找 return 一个 [Int]
,但显然不值得我最初的尝试。
如果你想 return 一个字符串,协议的解决方案绝对有效,这就是我接受答案的原因。
编辑 2
我们现在使用 type alias
!
得到了有效的答案
red
是一个 Colour
。你无能为力改变这一点。您所做的任何事情都不能使 red
直接充当包含 red
、green
和 blue
组件的 [Int]
。
根据您的实际需要,您可以执行不同的操作。例如,如果您只是想让它像 [Int]
一样工作以便像 [255, 0, 0]
那样打印,那么您可以覆盖 var description: String
并符合 CustomStringConvertible
.
您正在寻找的功能类似于 C++'s user-defined conversion operators。这些可能真的很酷,但也很容易导致非常不清楚的代码。 Swift 为了清楚起见,选择明确表达此类转换。
Swift 有一个名为 CustomDebugStringConvertible
的协议,它应该可以满足您的需求。在声明您的结构采用该协议后,只需在您的代码中实现 debugDescription
变量。
查看更新代码:
struct Colour: CustomDebugStringConvertible {
var red: Int
var green: Int
var blue: Int
var debugDescription: String {
return "\(array)"
}
var array: [Int] {
return [red, green, blue]
}
init(red: Int, green: Int, blue: Int) {
self.red = red
self.green = green
self.blue = blue
}
}
用法示例:
let red = Colour(red: 255, green: 0, blue: 0) // Colour
red.red // 255
red.green // 0
red.blue // 0
red // "[255, 0, 0]"
red.array // [255, 0, 0]
或者,您可以定义 typealias
颜色。这可能会更有效一些,但可能类型不安全。
见代码:
typealias Colour = [Int]
extension Array where Element == Int {
init(red: Int, green: Int, blue: Int) {
self = [red, green, blue]
}
var red: Int { return self.count > 0 ? self[0] : 0 }
var green: Int { return self.count > 1 ? self[1] : 0 }
var blue: Int { return self.count > 2 ? self[2] : 0 }
}
用法示例:
let red = Colour(red: 255, green: 0, blue: 0) // Colour
red.red // 255
red.green // 0
red.blue // 0
red // [255, 0, 0]
我建议添加一个函数,例如 toArray()
可能看起来像这样:
struct Colour {
var red: Int
var blue: Int
var green: Int
init(red: Int, green: Int, blue: Int) {
self.red = red
self.green = green
self.blue = blue
}
func toArray() -> [Int] {
return [self.red, self.green, self.blue]
}
}
然后,在您的代码中,您可以只使用 let rgbArray = MyColour.toArray()
此外,我建议不要将相同的基础数据存储两次,例如使用 rgb 变量。如果您更改 Colour.red 变量,这意味着您的 Colour.rgb 值不会自动更新为新值,您会突然取回您没有预料到的值。使用函数方法,您将不会遇到这个问题。
如果您想使 Colour
类型成为自定义集合,使您能够像 Array
一样使用它,但同时使其成为类型安全的,您可以创建一个自定义 struct
并使其符合Collection
。您可以创建一个 RGB
enum
可用于为自定义集合编制索引,以及一个可失败的初始化程序,以确保您只能创建具有有效 RGB 值的 Colour
实例。
struct Colour: Collection, RandomAccessCollection {
// Custom enum used for indexing Colour
enum RGB: Int, Hashable, Comparable, Strideable {
case red, green ,blue
// Find the last existing rawValue, assumes that the first case has rawValue 0 and that rawValues are incremented by 1
static let maxRawValue: RGB.RawValue = {
var maxRawVal = 0
while RGB(rawValue: maxRawVal) != nil {
maxRawVal += 1
}
return maxRawVal
}()
static let rawValues: [RGB.RawValue] = {
var rawValues = [RGB.RawValue]()
var currentRawValue = 0
while RGB(rawValue: currentRawValue) != nil {
rawValues.append(currentRawValue)
currentRawValue += 1
}
return rawValues
}()
static func <(lhs: RGB, rhs: RGB) -> Bool {
return lhs.rawValue < rhs.rawValue
}
typealias Stride = Int
func distance(to other: RGB) -> RGB.Stride {
return self.rawValue - other.rawValue
}
func advanced(by n: RGB.Stride) -> RGB {
return RGB(rawValue: (self.rawValue+n)%RGB.maxRawValue)!
}
}
typealias Element = Int
typealias Index = RGB
//Private backing Array
private var components:[Element] = Array<Element>.init(repeating: Element.init(), count: RGB.rawValues.count)
var startIndex: Colour.Index {
return RGB(rawValue: components.startIndex)!
}
var endIndex: Colour.Index {
return RGB(rawValue: components.endIndex)!
}
subscript (position: Index) -> Element {
get {
return components[position.rawValue]
}
set {
components[position.rawValue] = newValue
}
}
func index(after i: Index) -> Index {
return Index(rawValue: components.index(after: i.rawValue))!
}
private init(){}
//Failable initializer that makes sure only a 3 element Array can be used as an input and that each element is in the valid RGB color code range
init?<C:Collection>(_ collection:C) where C.Element == Element, C.Index == Index.RawValue {
guard collection.indices.map({[=10=]}) == RGB.rawValues else {return nil}
for (index, element) in collection.enumerated() {
guard element <= 255 && element >= 0 else {return nil}
self.components[index] = element
}
}
}
然后您就可以安全地使用您的自定义集合了:
let red = Colour([255,0,0]) // Colour
let invalidColour = Colour([255]) // nil
let invalidColour2 = Colour([255,2,3,4]) // nil
let invalidColour3 = Colour([255,-1,256]) // nil
let randomColour = Colour([1,5,231]) // Colour
// you can use the RGB cases to access the respective values in the collection
randomColour?[.red] // 1
我是 Swift 的新手,正在学习 struct
。但是,我有一个听起来很明显的问题,但我不确定该怎么做。我正在操场上做这一切。
我有一个名为 Colour
的 struct
,我在其中创建了 RGB 颜色类型。我想访问它的变量(例如,通过执行 yellow.red
这将读取和写入变量以找到黄色中红色的值)。
struct Colour {
var red: Int
var blue: Int
var green: Int
var rgb: [Int] // <- I don't want to have this variable, I want to make the struct Colour be this array
init(red: Int, green: Int, blue: Int) {
self.red = red
self.green = green
self.blue = blue
rgb = [red, green, blue]
}
}
致电:
let red = Colour(red: 255, green: 0, blue: 0) // Colour
red.red // 255
red.green // 0
red.blue // 0
red // Colour
red.rgb // [255, 0, 0]
当我访问 red
时,我希望它自动 return red.rgb
的值,而不需要变量。
那么我如何调用 red
和 return 一个值为 [255, 0, 0]
的 Array
?
备注:
无法实现
get
或set
我不能使用
Colour
作为变量,因为我需要初始化
无法使用 return
关键字,因为struct
不能以这种方式工作
编辑
抱歉没有说清楚。我一直在寻找 return 一个 [Int]
,但显然不值得我最初的尝试。
如果你想 return 一个字符串,协议的解决方案绝对有效,这就是我接受答案的原因。
编辑 2
我们现在使用 type alias
!
red
是一个 Colour
。你无能为力改变这一点。您所做的任何事情都不能使 red
直接充当包含 red
、green
和 blue
组件的 [Int]
。
根据您的实际需要,您可以执行不同的操作。例如,如果您只是想让它像 [Int]
一样工作以便像 [255, 0, 0]
那样打印,那么您可以覆盖 var description: String
并符合 CustomStringConvertible
.
您正在寻找的功能类似于 C++'s user-defined conversion operators。这些可能真的很酷,但也很容易导致非常不清楚的代码。 Swift 为了清楚起见,选择明确表达此类转换。
Swift 有一个名为 CustomDebugStringConvertible
的协议,它应该可以满足您的需求。在声明您的结构采用该协议后,只需在您的代码中实现 debugDescription
变量。
查看更新代码:
struct Colour: CustomDebugStringConvertible {
var red: Int
var green: Int
var blue: Int
var debugDescription: String {
return "\(array)"
}
var array: [Int] {
return [red, green, blue]
}
init(red: Int, green: Int, blue: Int) {
self.red = red
self.green = green
self.blue = blue
}
}
用法示例:
let red = Colour(red: 255, green: 0, blue: 0) // Colour
red.red // 255
red.green // 0
red.blue // 0
red // "[255, 0, 0]"
red.array // [255, 0, 0]
或者,您可以定义 typealias
颜色。这可能会更有效一些,但可能类型不安全。
见代码:
typealias Colour = [Int]
extension Array where Element == Int {
init(red: Int, green: Int, blue: Int) {
self = [red, green, blue]
}
var red: Int { return self.count > 0 ? self[0] : 0 }
var green: Int { return self.count > 1 ? self[1] : 0 }
var blue: Int { return self.count > 2 ? self[2] : 0 }
}
用法示例:
let red = Colour(red: 255, green: 0, blue: 0) // Colour
red.red // 255
red.green // 0
red.blue // 0
red // [255, 0, 0]
我建议添加一个函数,例如 toArray()
可能看起来像这样:
struct Colour {
var red: Int
var blue: Int
var green: Int
init(red: Int, green: Int, blue: Int) {
self.red = red
self.green = green
self.blue = blue
}
func toArray() -> [Int] {
return [self.red, self.green, self.blue]
}
}
然后,在您的代码中,您可以只使用 let rgbArray = MyColour.toArray()
此外,我建议不要将相同的基础数据存储两次,例如使用 rgb 变量。如果您更改 Colour.red 变量,这意味着您的 Colour.rgb 值不会自动更新为新值,您会突然取回您没有预料到的值。使用函数方法,您将不会遇到这个问题。
如果您想使 Colour
类型成为自定义集合,使您能够像 Array
一样使用它,但同时使其成为类型安全的,您可以创建一个自定义 struct
并使其符合Collection
。您可以创建一个 RGB
enum
可用于为自定义集合编制索引,以及一个可失败的初始化程序,以确保您只能创建具有有效 RGB 值的 Colour
实例。
struct Colour: Collection, RandomAccessCollection {
// Custom enum used for indexing Colour
enum RGB: Int, Hashable, Comparable, Strideable {
case red, green ,blue
// Find the last existing rawValue, assumes that the first case has rawValue 0 and that rawValues are incremented by 1
static let maxRawValue: RGB.RawValue = {
var maxRawVal = 0
while RGB(rawValue: maxRawVal) != nil {
maxRawVal += 1
}
return maxRawVal
}()
static let rawValues: [RGB.RawValue] = {
var rawValues = [RGB.RawValue]()
var currentRawValue = 0
while RGB(rawValue: currentRawValue) != nil {
rawValues.append(currentRawValue)
currentRawValue += 1
}
return rawValues
}()
static func <(lhs: RGB, rhs: RGB) -> Bool {
return lhs.rawValue < rhs.rawValue
}
typealias Stride = Int
func distance(to other: RGB) -> RGB.Stride {
return self.rawValue - other.rawValue
}
func advanced(by n: RGB.Stride) -> RGB {
return RGB(rawValue: (self.rawValue+n)%RGB.maxRawValue)!
}
}
typealias Element = Int
typealias Index = RGB
//Private backing Array
private var components:[Element] = Array<Element>.init(repeating: Element.init(), count: RGB.rawValues.count)
var startIndex: Colour.Index {
return RGB(rawValue: components.startIndex)!
}
var endIndex: Colour.Index {
return RGB(rawValue: components.endIndex)!
}
subscript (position: Index) -> Element {
get {
return components[position.rawValue]
}
set {
components[position.rawValue] = newValue
}
}
func index(after i: Index) -> Index {
return Index(rawValue: components.index(after: i.rawValue))!
}
private init(){}
//Failable initializer that makes sure only a 3 element Array can be used as an input and that each element is in the valid RGB color code range
init?<C:Collection>(_ collection:C) where C.Element == Element, C.Index == Index.RawValue {
guard collection.indices.map({[=10=]}) == RGB.rawValues else {return nil}
for (index, element) in collection.enumerated() {
guard element <= 255 && element >= 0 else {return nil}
self.components[index] = element
}
}
}
然后您就可以安全地使用您的自定义集合了:
let red = Colour([255,0,0]) // Colour
let invalidColour = Colour([255]) // nil
let invalidColour2 = Colour([255,2,3,4]) // nil
let invalidColour3 = Colour([255,-1,256]) // nil
let randomColour = Colour([1,5,231]) // Colour
// you can use the RGB cases to access the respective values in the collection
randomColour?[.red] // 1