是否有可能创建对结构数组元素的引用?

Posted

技术标签:

【中文标题】是否有可能创建对结构数组元素的引用?【英文标题】:Is it ever possible to create a reference to element of array of struct? 【发布时间】:2020-12-08 15:28:45 【问题描述】:

给定一个Arraystruct

    import Foundation
    
    struct Card 
        var flag: String = ""
    
    
    var cards = Array<Card>()
    cards.append(Card())

以下操作不会修改原始数组元素

    // A copy is created.
    var cardCopy = cards[0]
    
    // Will NOT modify cards[0] 
    cardCopy.flag = "modify0"
    
    print(cards[0].flag)

下面的操作会修改原来的数组元素

    // We can modify cards[0] by
    cards[0].flag = "modify"

    print(cards[0].flag)

但是,从某种意义上说它并不高效,我们每次都需要执行索引访问。想象一下

    cards[0].flag0 = "modify"
    cards[0].flag1 = "modify"
    cards[0].flag2 = "modify"
    cards[0].flag3 = "modify"
    ...

有没有办法,我们可以创建对结构数组元素的引用?这样我们就可以编写

// How to create a reference to cards[0]?
var cardReference = ...
    cardReference.flag0 = "modify"
    cardReference.flag1 = "modify"
    cardReference.flag2 = "modify"
    cardReference.flag3 = "modify"
    ...

其中一种可能性是将struct 替换为class。但是,在此之前,我想探索其他替代方案。

【问题讨论】:

如果你需要修改你的 Card 实例很多,也许你应该把它变成一个类,或者用新值创建一个新实例并替换数组中的整个对象。 @JoakimDanielson 谢谢。在将其从结构转换为类之前,我想探索其他可能的替代方案(可能创建引用?)。 您不能将值类型转换为引用类型。正如其他人指出的那样,inout 与您将要获得的一样接近。 【参考方案1】:

您可以使用函数进行更改并通过引用传递Card 结构,如下所示:

func update(card: inout Card) 
    card.flag0 = "modify"
    card.flag1 = "modify"
    card.flag2 = "modify"
    card.flag3 = "modify"


var cards = Array<Card>()
cards.append(Card())

update(card: &cards[0])

甚至更好的是在Card 类型中使用变异函数并将您的更改作为闭包传递:

struct Card 
    var flag0: String = ""
    var flag1: String = ""
    var flag2: String = ""
    var flag3: String = ""
    
    mutating func update(block: (inout Card) -> Void) 
        block(&self)
    

    
var cards = Array<Card>()
cards.append(Card())

cards[0].update 
    $0.flag0 = "modify"
    $0.flag1 = "modify"
    $0.flag2 = "modify"
    $0.flag3 = "modify"

更新:为了使第二种方法更加可重用,您可以定义如下协议:

protocol Updatable 
    mutating func update(block: (inout Self) -> Void)


extension Updatable 
    mutating func update(block: (inout Self) -> Void) 
        block(&self)
    

并使 Card 结构符合它:

struct Card: Updatable 
    var flag0: String = ""
    var flag1: String = ""
    var flag2: String = ""
    var flag3: String = ""

那么你就可以像上面一样使用它了。

【讨论】:

我真的很喜欢这种使结构通常可以自我更新的技巧;我不敢相信我从来没有想过这个。 :) 我想把这个想法写进我的书中(当然归功于你);如果可以,请告诉我。 @matt 我做了一些研究,关于我第一次看到这样的东西并发现这个gist.github.com/nicklockwood/9b4aac87e7f88c80e932ba3c843252df。所以,对我来说这当然没问题,但公平地说,这并不完全是我的想法。 好吧,你的想法并不是真正取自那个要点,所以我仍然认为你得到了信任。 @matt 好吧,是的,这就像相同技术的另一种用法。【参考方案2】:

这是预期的行为,因为Arraystructstructs 是值类型。

您需要的是引用类型行为,因此您应该将您的 struct 转换为 class

一次性修改值类型的多个属性的另一种解决方案是创建一个 mutating 函数来执行此操作并在数组元素上调用它。

struct Card 
    var flag: String = ""
    var flag2 = ""

    mutating func update(flag: String, flag2: String) 
        self.flag = flag
        self.flag2 = flag2
    


var cards = [Card(flag: "a", flag2: "b")]
cards[0].update(flag: "b", flag2: "c")

【讨论】:

以上是关于是否有可能创建对结构数组元素的引用?的主要内容,如果未能解决你的问题,请参考以下文章

c#如何将结构的引用添加到数组或列表?

数据结构之数组

结构体数组怎么做函数参数

二维数组结构段错误

根据索引和值从数组元素创建结构

如何在 ColdFusion 中对结构数组进行排序