Return value from struct when used

时间:2018-05-17 19:33:08

标签: swift struct swift-playground

I am quite new to Swift and I am learning about structs. 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 structs 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!

5 个答案:

答案 0 :(得分:2)

redColour。你无能为力改变这一点。您所做的任何操作都无法使red直接作为[Int]包含redgreenblue组件。

根据您的实际需求,您可以做不同的事情。例如,如果您只是希望它像[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
        }
    }
}