I am quite new to Swift and I am learning about struct
s. However, I have a problem which might sound obvious, but I'm not sure how to do it. I am doing all this in the playground.
I have a struct
called Colour
, where I create an RGB colour type. I want to access its variables (e.g. by doing yellow.red
which will read and write the variable to find the value of red in the colour yellow).
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]
}
}
Call:
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]
When I access red
, I want it to automatically return the value of red.rgb
, without the variable.
So how can I call red
and return an Array
with the value [255, 0, 0]
?
Notes:
A get
or set
cannot be implemented
I cannot use Colour
as a variable, as I need initialisation
return
keyword cannot be used, as struct
s don't work in this way
Edit
Sorry for not making this clear enough. I was looking to return a [Int]
, but it is clearly not worth what I was originally trying.
The solutions with the protocols definitely work if you want to return a string, which is why I have accepted an answer.
Edit 2
We now have a working answer using type alias
!
答案 0 :(得分:2)
red
是Colour
。你无能为力改变这一点。您所做的任何操作都无法使red
直接作为[Int]
包含red
,green
和blue
组件。
根据您的实际需求,您可以做不同的事情。例如,如果您只是希望它像[Int]
一样打印,那么您可以覆盖[255, 0, 0]
并符合CustomStringConvertible
。
您正在寻找的功能类似于C++'s user-defined conversion operators。这些可能非常酷,但也很容易导致代码非常不清晰。为清楚起见,Swift选择明确表达此类转换。
答案 1 :(得分:2)
或者,您可以定义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]
答案 2 :(得分:1)
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]
答案 3 :(得分: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值不会自动更新该值,并且您会突然找回您不期望的值。使用函数方法,您不会遇到这个问题。
答案 4 :(得分:0)
如果您想让Colour
键入自定义集合,让您像Array
一样使用它,但同时使其类型安全,则可以创建自定义{{1并使其符合struct
。您可以创建可用于索引自定义集合的Collection
RGB
和可用的初始值设定项,以确保您只能创建具有有效RGB值的enum
实例。
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({$0}) == RGB.rawValues else {return nil}
for (index, element) in collection.enumerated() {
guard element <= 255 && element >= 0 else {return nil}
self.components[index] = element
}
}
}