如何在 Swift 中抛出二传手的错误?

How to throw errors from a setter in Swift?

提问人:rayaantaneja 提问时间:9/10/2023 最后编辑:rayaantaneja 更新时间:9/10/2023 访问量:60

问:

我想知道是否有办法让 setter 在 Swift 中抛出错误。我认为这可以解决我的问题,但我找不到一种方法,这让我怀疑我是否以正确的方式构建了我的代码。这是我试图解决的问题:

我有一个名为的结构,用户可以从他们的终端进行编辑Account

struct Account {
    var strikes: Int = 0
    
    private var _monetizationLevel: Int = 1
    
    var monetizationLevel: Int {
        get { _monetizationLevel }
        set {
            guard strikes == 0 else { print("Please resolve copyright strike before adjusting moentization level"); return }
            guard newValue <= _monetizationLevel else { print("Monetization level can only be increased by a server admin"); return }
            _monetizationLevel = newValue
        }
    }
}

使用 Swift 对 setter 的内置支持感觉就像是编辑帐户特征的一种自然方式。例如。,(或其他)myAccount.name = "newName123"myAccount.monetizationLevel -= 4

但是,由于对您可以编辑的内容有一定的限制,因此我认为抛出错误将是一个合适的解决方案(因为简单地打印警告感觉非常弱)。不幸的是,我无法找到让二传手抛出错误的方法。我考虑过使用抛出函数,但考虑到 Swift 有专门用于消除使用函数设置器的尴尬的内置函数,使用函数设置器似乎真的很愚蠢。为了保持一致性,我更喜欢保留 、 等的自然性的 Swift-y 解决方案。monetizationLevelsetMonetizationLevel(to: Int)myAccount.name = "blah"myAccount.monetizationLevel -= 5

另一方面,如果不使用函数设置器就无法实现这一点,那么重组结构的更好方法是什么?你们会怎么做?Account

iOS Swift 数据库 数据结构 错误处理

评论

0赞 Joakim Danielson 9/10/2023
它不起作用,即使它起作用了,它也不会像您建议的那样迅速,因为您需要使用并且可能还需要用 do/catch 来包围任务。在我看来,如果 get 或 set 包含很多逻辑,也可能是有问题的,所以我的建议是改用函数。try
1赞 rayaantaneja 9/10/2023
@JoakimDanielson真的。使用“try”并不理想,但就像我说的,我认为错误处理是找出“set”失败原因的最合适的解决方案。你对我如何重构我的代码有什么建议吗?
0赞 Joakim Danielson 9/10/2023
请参阅我编辑的评论。
1赞 rayaantaneja 9/10/2023
@JoakimDanielson啊,很不幸,我不得不使用一个函数。我将尝试集思广益,通过一种方法来重构我的代码,因为我不愿意只为这个属性使用一个 setter 函数。不一致真的会打扰我:/谢谢你的建议!
0赞 Paulw11 9/10/2023
二传手的合同很简单——它设置一个属性。您需要一个更复杂的合约 - 操作可能会失败/抛出。这意味着您需要一个函数。可能是这个业务逻辑不属于 - 也许它属于其他地方。例如,您在哪里防止设置为?也许你根本不应该使用二传手?也许应该有一些函数可以增加罢工次数和/或减少/归零计数。 然后将是一个属性Accountstrikes-1recordStrike()clearStike()clearAllStrikes()stikesprivate (set)

答:

2赞 Paulw11 9/10/2023 #1

这已经在评论中讨论过了,没有二传手不能 - 二传手的合同很简单 - 属性值是设置的。编译器没有办法知道可以.throwaccount.monetizationLevelthrow

您需要考虑的是要在何处实现业务逻辑。看起来你正在尝试在这个结构中实现业务逻辑,你说“(混合 setter 和函数)的不一致真的会困扰我:/”

如果你打算在这个结构中实现业务逻辑,那么你可能根本不应该使用 setter。大概有一些业务逻辑 - 即它们应该是 >=0strikes

也许记录罢工也应该减少货币化?

此外,您的逻辑表明只有管理员才能增加货币化,但设置者无法知道调用者是否是管理员 - 您可以将其提供给函数。

将属性公开为只读并使用函数进行设置可能是一种更好的方法。我认为它会产生一个更易于理解的 API,并将操作与实现分开。

例如:

enum AccountException: Error {
    case BadCopyrightStanding
    case NotPermitted
}

struct Account {
    private (set) var strikes: Int
    private (set) var monetizationLevel: Int
    var hasStrikes: Bool {
       return strikes != 0
    }
    mutating func recordStrike() {
        self.strikes+=1
        self.monetizationLevel = 1
    }
    
    mutating func clearStrike() {
        self.strikes = max(self.strikes-1,0)
    }
    
    mutating func clearStrikes() {
        self.strikes = 0
    }
    
    mutating func setMonitizationLevel(_ level:Int, isAdmin: Bool) throws {
        guard self.strikes == 0 else {
            throw AccountException.BadCopyrightStanding
        }
        guard isAdmin || level <= self.monitizationLevel else {
            throw AccountException.NotPermitted
        }

        self.monetizationLevel = level
    }
}

或者,如果你在其他地方实现业务逻辑,那么这应该只是一个“愚蠢的结构”,它不应该强制执行罢工和货币化之间的关系。