有什么方法可以使方法 return 成为可变值吗?

Is there any way to make the method return a mutable value?

如下代码所示:

struct Person {
    var name: String
}

struct Group {
    var person: Person
    
    func callAsFunction() -> Person {
        // Person is immutable value
        person
    }
}

var james = Person(name: "James")
var group = Group(person: james)
group().name = "Wong" //ERROR: Cannot assign to property: function call returns immutable value

group() return 一个不可变的值,无法更改!那么有没有办法让 callAsFunction() 方法 return 成为一个可变值?

谢谢 ;)


已更新:

我的想法是将Group的所有调用和访问都转移到Group中的Person对象上,就像直接使用Person一样

我无法使用 dynamicMemberLookup,因为我不知道 Person 中会有什么方法或 属性。比如Person中可能有100个方法和属性(不是演示的只有一个名字属性),我不可能用dynamicMemberLookup写100个下标方法

我的需求有点像Ruby语言中的代理对象。访问一个对象(Group)实际上是访问了其中的另一个对象(Person),就好像Group不存在一样。

ruby 代理模式: https://refactoring.guru/design-patterns/proxy/ruby/example

CallAsFunction是目前为止最接近的实现,但是要求Person不能是Struct,否则不能赋值给它的属性。

也许 Swift 还不能实现这个功能?

您使用了错误的动态方法。你要的是dynamicMemberLookup。盯紧了。一、准备工作:

struct Person {
    var name: String
}

@dynamicMemberLookup
struct Group {
    var person: Person
    subscript(dynamicMember kp:WritableKeyPath<Person,String>) -> String {
        get { self.person[keyPath:kp] }
        set { self.person[keyPath:kp] = newValue }
    }
}

现在看看你能说什么:

var group = Group(person: Person(name: "James"))
group.name = "Wong"
print(group.person) // Person(name: "Wong")

看到了吗?我们设置了Group的name,即使它没有name 属性,结果是我们设置了Group的personname有没有name属性.

callAsFunction只是returns(复制)Person,这是一个值类型。然后你不能像那样改变它的 属性 。它等效于以下内容:

struct Person {
    var name: String
}

Person(name: "Foo").name = "Bar"

即returns同样的错误:

如果 Person 是引用类型,它会起作用,但不适用于值类型。即使你采用了你的值类型,并在改变它之前先将它分配给一个变量,你也只会改变你的副本,而不是原始的。

如果您想要您想要的行为,您可以使用 @dynamicMemberLookup,如 matt (+1) 所建议并在 SE-0195 中概述的那样。


你说:

I can't use dynamicMemberLookup because I don't know what method or property there will be in Person. For example, there may be 100 methods and properties in Person (not only one name property as demonstrated), and it is impossible for me to write 100 subscript methods with dynamicMemberLookup.

你不需要“100 种下标方法”。这是 @dynamicMemberLookup 背后的激励思想,即属性将被动态确定。例如,这里 Person 有两个属性,但 Group 只有一个 @dynamicMemberLookup.

struct Person {
    var name: String
    var city: String
}

@dynamicMemberLookup
struct Group {
    var person: Person
    subscript(dynamicMember keyPath: WritableKeyPath<Person, String>) -> String {
        get { person[keyPath: keyPath] }
        set { person[keyPath: keyPath] = newValue }
    }
}

var group = Group(person: Person(name: "James", city: "New York"))
group.name = "Wong"
group.city = "Los Angeles"
print(group.person) // Person(name: "Wong", city: "Los Angeles")

如果要处理不同的类型,请将其设为泛型:

struct Person {
    var name: String
    var city: String
    var age: Int
}

@dynamicMemberLookup
struct Group {
    var person: Person
    subscript<T>(dynamicMember keyPath: WritableKeyPath<Person, T>) -> T {
        get { person[keyPath: keyPath] }
        set { person[keyPath: keyPath] = newValue }
    }
}

var group = Group(person: Person(name: "James", city: "New York", age: 41))
group.name = "Wong"
group.city = "Los Angeles"
group.age = 42
print(group.person) // Person(name: "Wong", city: "Los Angeles", age: 42)