Return 使用时来自结构的值

Return value from struct when used

我是 Swift 的新手,正在学习 struct。但是,我有一个听起来很明显的问题,但我不确定该怎么做。我正在操场上做这一切。

我有一个名为 Colourstruct,我在其中创建了 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

备注:

编辑

抱歉没有说清楚。我一直在寻找 return 一个 [Int],但显然不值得我最初的尝试。

如果你想 return 一个字符串,协议的解决方案绝对有效,这就是我接受答案的原因。

编辑 2

我们现在使用 type alias!

得到了有效的答案

red 是一个 Colour。你无能为力改变这一点。您所做的任何事情都不能使 red 直接充当包含 redgreenblue 组件的 [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