Swift 如何创建一个既能保存UInt值又能保存附加值00的类型?

Swift 如何创建一个既能保存UInt值又能保存附加值00的类型?,swift,Swift,我在尝试创建一个包含一组特定值的类型时遇到了一个问题。这是一个篮球应用程序 篮球运动员有球衣号码。在一些欧洲国家(如本应用程序),数字范围为0到99(含)。但还有一个附加值00可用,它与0不同 我可以使用字符串,但字符串可以存储其他值,我必须编写额外的验证代码 现在我正在使用UInt来存储数字 是否有更好的方法将此类值存储为Swift类型?您可以使用如下枚举: enum JerseyNumber { case jersey(UInt) case special //You could

我在尝试创建一个包含一组特定值的类型时遇到了一个问题。这是一个篮球应用程序

篮球运动员有球衣号码。在一些欧洲国家(如本应用程序),数字范围为0到99(含)。但还有一个附加值00可用,它与0不同

我可以使用字符串,但字符串可以存储其他值,我必须编写额外的验证代码

现在我正在使用UInt来存储数字


是否有更好的方法将此类值存储为Swift类型?

您可以使用如下枚举:

enum JerseyNumber {
   case jersey(UInt)
   case special //You could call this doubleZero
}

这将允许您存储Jersey编号和此特殊00编号。

您可以定义字符串类型枚举并明确列出合法值:

enum JerseyNumber: String {
    case jersey00 = "00"
    case jersey0 = "0"
    case jersey1 = "1"
    case jersey2 = "2"
    case jersey3 = "3"
    case jersey4 = "4"
    case jersey5 = "5"
    //...
    case jersey99 = "99"
    case jersey100 = "100"
}
然后,要获取JerseyNumber的字符串值:

print (JerseyNumber.jersey00.rawValue)
(产出“00”)

要从字符串创建运动衫编号,请执行以下操作:

if let aJersey = JerseyNumber(rawValue: "5") {
    print(aJersey)
}

(输出“jersey5”{枚举的名称,而不是它的rawValue})

这是自定义类型的一个很好的用例。它绝对不是UInt,也不是任何类型的整数。是23+#24=#47?那是胡说八道。什么是#99*#2?再说一遍,胡言乱语。这不是一个整数

更进一步,虽然“00”是一个合法的球衣号码,“01”不是。这和“1”不一样。这不是一个有效的数字。事实上,如果这是NCAA(见第5节第5.b.2条)

相反,JerseyNumber是它自己的类型。它有自己的法律操作(从技术上讲,它有自己的代数)

JerseyNumber最合理的基本类型是String,因为您希望能够区分00和0。我可能会以这种方式实现它,这将为您提供equalable、Hashable、CustomStringConvertible和更方便的Codable:

struct JerseyNumber: Hashable {
    private var string: String

    static let legalNumbers = ["0", "1", "2", "3", "4", "5", "00", "10", "11", "12", "13", "14", "15", "20", "21", "22", "23", "24", "25", "30", "31", "32", "33", "34", "35", "40", "41", "42", "43", "44", "45", "50", "51", "52", "53", "54", "55"]

    init?(_ string: String) {
        guard JerseyNumber.legalNumbers.contains(string) else { return nil }
        self.string = string
    }
}

extension JerseyNumber: CustomStringConvertible {
    var description: String { string }
}

extension JerseyNumber: Codable {
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        let value = try container.decode(String.self)
        guard let number = JerseyNumber(value) else {
            throw DecodingError.dataCorruptedError(in: container,
                                                   debugDescription: "Could not decode JerseyNumber: \(value)")
        }
        self = number
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(string)
    }
}

显然,如果这不是NCAA,或者如果您想为您的数字制定其他规则,您可以修改
init
,以强制执行该规则,或者以对您的问题有意义的任何方式不强制执行。例如,您可以解析字符串,而不是拥有一个大的合法值列表。这只是一个例子。但是基本要点是为此使用自定义类型,而不是UInt。

这里有一个使用属性包装的稍微不同的解决方案

@propertyWrapper struct JerseyNumber {
    var wrappedValue: Int?

    init(_ number: Int) {
        guard (0...100).contains(number) else {
            wrappedValue = nil
            return
        }
        wrappedValue = number
    }

    var projectedValue: String {
        switch wrappedValue {
        case nil: return "not set"
        case 100: return "00"
        default:
            return String(wrappedValue!)
        }
    }
}

    struct Player {
    let name: String
    @JerseyNumber var number: Int?

    var description: String {
        "\(name) \($number)"
    }
}

var team = [
    Player(name: "A", number: JerseyNumber(10)),
    Player(name: "B", number: JerseyNumber(15)),
    Player(name: "C", number: JerseyNumber(3)),
    Player(name: "D", number: JerseyNumber(100)),
    Player(name: "E", number: JerseyNumber(1000)),
]

team.forEach { print($0.name, $0.$number) }

将范围增加到0到100,让100成为那个特殊的数字。然后在显示时添加一些逻辑,使100显示为00。如果我们能看到您正在创建的这个特殊“类型”,这将非常有帮助。首先,你是如何将数字限制在0…99范围内的?@matt现在它是一个没有任何上限检查的UInt,因为它不是那么重要,我已经编辑了问题,好吧,所以在你的问题标题和问题主体中“创建类型”不是真的吗?没关系,只是想弄清楚。如果你想用普通的UInt,那很好。你可以按照约阿基姆·丹尼尔森的建议将其扩展为字符串。@PhillipMills是的,这是NBA的规则,但例如FIBA有4…15,在一些欧洲联赛中你可以使用00,0…99这如何解决限制UInt范围的问题?是否说必须限制数字?问题提到了范围,但实际问题是如何存储这些值。@限制Uint的范围不是我关心的问题,这主要是关于区分0和00的能力。在这一点上,我真的不明白为什么这比字符串上的验证包装器更好。它允许您验证给定值是否被允许,它允许您使用穷举开关语句。为什么不在字符串上发布一个验证包装器的示例?(不太清楚你的意思,所以我想看看。)我想我的答案就是@matt所说的。明白了。我觉得你说的是自定义类型。这是我一直在考虑的解决方案。想法:也许某种依赖注入(我讨厌这个词)可以用来实现欧洲和美国的编号规则。此外,我认为您应该展示如何按规则生成
legalNumbers
,即使用表示规则内容的代码,而不是一个明显容易出错的手动值列表。对于NCAA规则,我认为这种方法最不容易出错,因为它是直接从规则复制的(列出确切的允许数字)。制定一个匹配这些数字的算法,而不是按照规定列出规则,这是可能出现错误的地方。但当然,在其他环境中,创建一个通用形式,如
struct JerseyNumber
,可能会很有用,其中
RuleSet
是一个提供
静态函数有效的协议(字符串:字符串)->Bool
。使其更具可定制性的最大折衷是,这会使序列化变得困难。如果您知道程序中只存在一个规则系统,则可以将数字编码为字符串。但如果可能存在不同的规则,则在序列化时可能需要对其类型进行编码。我会等到真正需要时再进行编码t。这看起来不错@RobNapier,谢谢。我还看到了如何按照@matt的建议生成
legalNumbers
并使其更易于测试,从而轻松地适应其他编号规则。我会将您的答案标记为已接受。为什么不使用集合而不是数组?看起来顺序无关紧要。