在 Swift 中更新不同数据类型属性的通用方法

问题描述 投票:0回答:3

我不知道是否可能,但我认为可以有办法做我想做的事。
我有以下情况:

enum ContactStatus: UInt {
    case requestSent = 0, requestReceived, requestProcessing, active, removed
}

struct Contact {
    var uid: String
    var name: String?
    var contactStatus: ContactStatus

    init(uid: String, name: String? = nil, contactStatus: ContactStatus = .requestSent) {
        self.uid = uid
        self.name = name
        self.contactStatus = contactStatus
    }
}

class ContactStorage: {
    private var contacts: [Contact]
    init(contacts: [Contact]) {
        contacts = contacts
    }

    func createContact(uid: String, contactStatus status: ContactStatus, name: String?) -> Contact {
        var newContact = Contact(uid: uid)
        newContact.contactStatus = status
        newContact.name = name
        newContacts.append(newContact)
        return newContact
    }
    func updateContact(status: ContactStatus, peerUId: String) {
        for (index, _) in contacts.enumerated() {
            if contacts[index].uid == peerUId {
                contacts[index].contactStatus = status
                return
            }
        }
    }
    func updateContact(name: String, peerUId: String) {
        for (index, _) in contacts.enumerated() {
            if contacts[index].uid == peerUId {
                contacts[index].name = name
                return
            }
        }
    }
}

正如我们所看到的,

ContactStorage
有两种方法来更新联系人状态和姓名。
我希望使用通用数据类型或任何其他可能的方式将这两种方法合并为一种方法。

类似:

func updateContact<T: Updateable>(peerUId: String, updatableProperty: T) {
    //implementation
}

是否可以只写一个方法而不是两个?

swift generics
3个回答
1
投票

你可以“有点”用闭包来做到这一点。

“XXX”和“YYY”部分是您所拥有的两种方法之间唯一不同的部分:

for (index, _) in contacts.enumerated() {
    if contacts[index].uid == peerUId {
        contacts[index].XXX = YYY
        return
    }
}

因此,如果我们想将其提取为方法,我们需要一个闭包作为参数来提供“XXX”部分。

该闭包可以如下所示:

(inout Contact, T) -> Void

对于“YYY”部分,常规参数就可以了。

因此,该方法如下所示:

func updateProperty<T>(property: (inout Contact, T) -> Void, value: T, peerUId: String) {
    for (index, _) in contacts.enumerated() {
        if contacts[index].uid == peerUId {
            property(&contacts[index], value)
            return
        }
    }
}

你可以这样称呼它:

updateProperty(property: {$0.name = $1}, value: "Tom", peerUId: "something")

另一种方法是完全删除“YYY”,并将其集成到闭包中:

func updateProperty(property: (inout Contact) -> Void, peerUId: String) {
    for (index, _) in contacts.enumerated() {
        if contacts[index].uid == peerUId {
            property(&contacts[index])
            return
        }
    }
}

用途:

updateProperty(property: {$0.name = "Tom"}, peerUId: "something")

0
投票

不需要通用函数。您应该让

updateContact
有 3 个输入参数:
peerUId
name
status
,并将后两个设置为具有
nil
默认值的可选参数。这样,您可以使用单个函数调用来更新
name
status
,如果您只想更新其中一个,则只需不为另一个指定输入值即可。

func updateContact(peerUId: String, status: ContactStatus? = nil, name: String? = nil) {
    if status == nil, name == nil { return } //wrong function call, return
    for i in 0..<contacts.count {
        if contacts[i].uid == peerUId {
            if let newStatus = status {
                contacts[i].contactStatus = newStatus
            }
            if let newName = name {
                contacts[i].name = newName
            }
        }
    }
}

您可以这样调用该函数:

updateContact(peerUId: "value", status: ContactStatus.active) //only updates the contactStatus

updateContact(peerUId: "value", name: "John") //only updates the name

updateContact(peerUId: "value", status: ContactStatus.active, name: "John") //updates both in a single function call

0
投票

我知道这是一篇非常旧的帖子,但是在寻找答案时,我碰巧偶然发现了这个问题,并找到了一些实现来创建“可更新”协议。

协议定义 -

protocol UpdatableProperties {}

protocol Updatable {
    associatedtype Property: UpdatableProperties
    func update(property: Property)
}

像这样使用这些协议 -

enum ObjectUpdatableProperties: UpdatableProperties {
    case name(name: String)
    case phoneNumber(phoneNumber: String)
    case email(email: String)
}

class UpdatableObject: Updatable {
    private(set) var userId: String
    private(set) var userEmail: String?
    private(set) var name: String?
    private(set) var phoneNumber: String?
    
    init(userId: String, userEmail: String? = .none, name: String? = .none, phoneNumber: String? = .none) {
        self.userId = userId
        self.userEmail = userEmail
        self.name = name
        self.phoneNumber = phoneNumber
    }
    
    func update(property: ObjectUpdatableProperties) {
        switch property {
        case .name(let name):
            self.name = name
        case .phoneNumber(let phoneNumber):
            self.phoneNumber = phoneNumber
        case .email(let email):
            self.userEmail = email
        }
    }
}

使用-

var updatableObj = UpdatableObject(userId: "userId", userEmail: "[email protected]", name: "My Name Is Me", phoneNumber: .none)

updatableObj.update(property: .phoneNumber(phoneNumber: "A real phone number!"))

希望这对某人有帮助...

© www.soinside.com 2019 - 2024. All rights reserved.