为什么枚举在 Swift 中有计算属性但没有存储属性?
Why do enums have computed properties but not stored properties in Swift?
我是 Swift 的新手,刚在文档中看到这个:
Computed properties are provided by classes, structures, and
enumerations. Stored properties are provided only by classes and
structures.
这是为什么?枚举的关联值是否像存储属性一样工作?看起来他们最初存储了属性 - Why no stored type properties for classes in swift?
枚举被认为是一种结构化数据类型,无需在代码中多次更改 String 或 Int 即可对其进行修改,使用枚举,我们永远不必担心多次更改同一事物.例如下拉菜单:
enum DropDownMenuOptions: String {
case MenuOptionOne
case MenuOptionTwo
case MenuOptionThree
}
使用存储的属性,您可以预先计算所需的信息并减少主函数中的代码。最好的例子是计算矩形的大小,例如:
struct Point {
var x = 0.0, y = 0.0
}
struct Size {
var width = 0.0, height = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set(newCenter) {
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2)
}
}
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
size: Size(width: 10.0, height: 10.0))
enum
s 确实存储了 type 属性 - 即 static
属性。他们没有存储 instance 属性。我不知道是否有技术原因导致 enum
s 无法使用存储的实例属性。如果您想要 "why".
的技术答案,您可能需要在开发论坛上提问
在您的问题中,您询问关联值是否像存储属性一样工作。事实上,它们确实如此,并且比 struct
和 class
的存储属性更灵活(在某些方面)。 enum
中的每个 case
都可以有自己的专用数据集与之关联。您可以为每个 case
.
个性化存储属性,而不是拥有一组适用于所有 case
的存储属性
我使用了一个小技巧来存储初始化时不可访问的属性。
首先,我创建一个 class Future
来存储将来存储的 属性:
class Future<T> {
var value: T?
init(value: T? = nil) {
self.value = value
}
}
然后我创建我的枚举:
enum Sample {
case Test(future: Future<String>)
}
实例化:
let enumTest = Sample.Test(future: Future())
后面的代码:
switch enumTest {
case let .Test(future):
future.value = "Foo"
}
稍后,您可以访问值:
switch enumTest {
case let .Test(future):
// it will print Optional("Foo")
print("\(future.value)")
}
这不是您应该滥用的东西,但在某些情况下它会有所帮助。
希望对您有所帮助。
不允许存储实例属性的枚举是一种设计选择。具有存储实例属性的枚举使其像一个结构(具有枚举超能力),但从类型的角度来看,现在枚举将像类型乘法器一样工作。基本考虑
enum Set1 {
case a
case b
case c
}
enum Times {
case x
case y
var k: Set1
}
这实际上意味着枚举 Times
允许我们对 Set1
和 Set2
中的元素进行任意组合,从而产生 6 种不同的情况,但是等等,我们知道实际上这是一个像 (Set1, Set2)
这样的元组类型的用途,其中 Times
可以声明为
typealias Times = (Set1, Set2)
我希望这是不允许前一种情况的合理理由。
也就是说,swift 枚举允许我们将任意 n 元组与每个 case 相关联,从而允许我们在函数式中声明所谓的可区分联合编程。如果您愿意,可以将其称为附加到案例的存储 属性。从类型的角度来看,它现在充当类型加法器。
enum Add {
case lhs(Set1)
case rhs(Set2)
}
我们现在有 5 个不同的案例。如果我们现在存储 2 元组:
enum AddTimes {
case lhs(Set1, Set2)
case rhs(Set3, Set4)
}
我们现在基本上有了乘法和 (Set1 * Set2 + Set3 * Set4)。
在模式匹配方面,这是一个非常强大的工具。
HOWEVER,在一些实际情况下,您实际上想要模拟枚举中存储的 属性 的形式。考虑一下:
public enum Service {
case registerNewUser(username: String, password: String, language: String)
case login(username: String, password: String, deviceTokenº: String?)
case logout(sessionToken: String)
case sendForgotPassword(email: String)
}
是一种定义 REST 端点的声明方式(在 Moya 等框架中)
当你想触发一个请求时,你会做类似
的事情
MoyaProvider<Service>.request(.sendForgotPassword(email: "foo@foo.com"))
但现在假设您想区分生产服务器和测试服务器。如果在每种情况下都将服务器添加为另一个元组元素:
case forgotPassword(sessionToken: String, serverBaseURLString: String)
这将有一个错误的语义,因为您最初打算每个元组存储请求参数,但现在它存储一个服务器基址。
为了避免这样的事情,我们实际上可以通过以下方式参数化我们的类型。而不是将服务器定义为:
enum Server: String {
case production = "https://service.info"
case test = "http://test.service.info"
}
我们可以为每种情况定义不同的类型,例如:
public struct ProductionServer: ServerType {
public static var baseURLString: String { return "https://service.info" }
}
public struct TestServer: ServerType {
public static var baseURLString: String { return "http://test.service.info" }
}
public protocol ServerType {
static var baseURLString: String { get }
}
最后将我们的 ServiceType 参数化为
public enum Service<T> where T: ServerType {
case registerNewUser(username: String, password: String, language: String)
case login(username: String, password: String, deviceTokenº: String?)
case logout(sessionToken: String)
case sendForgotPassword(email: String)
var serverURL: URL {
return T.baseURL
}
}
public typealias ProdutionService = Service<ProductionServer>
public typealias TestService = Service<TestServer>
我是 Swift 的新手,刚在文档中看到这个:
Computed properties are provided by classes, structures, and enumerations. Stored properties are provided only by classes and structures.
这是为什么?枚举的关联值是否像存储属性一样工作?看起来他们最初存储了属性 - Why no stored type properties for classes in swift?
枚举被认为是一种结构化数据类型,无需在代码中多次更改 String 或 Int 即可对其进行修改,使用枚举,我们永远不必担心多次更改同一事物.例如下拉菜单:
enum DropDownMenuOptions: String {
case MenuOptionOne
case MenuOptionTwo
case MenuOptionThree
}
使用存储的属性,您可以预先计算所需的信息并减少主函数中的代码。最好的例子是计算矩形的大小,例如:
struct Point {
var x = 0.0, y = 0.0
}
struct Size {
var width = 0.0, height = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set(newCenter) {
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2)
}
}
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
size: Size(width: 10.0, height: 10.0))
enum
s 确实存储了 type 属性 - 即 static
属性。他们没有存储 instance 属性。我不知道是否有技术原因导致 enum
s 无法使用存储的实例属性。如果您想要 "why".
在您的问题中,您询问关联值是否像存储属性一样工作。事实上,它们确实如此,并且比 struct
和 class
的存储属性更灵活(在某些方面)。 enum
中的每个 case
都可以有自己的专用数据集与之关联。您可以为每个 case
.
case
的存储属性
我使用了一个小技巧来存储初始化时不可访问的属性。
首先,我创建一个 class Future
来存储将来存储的 属性:
class Future<T> {
var value: T?
init(value: T? = nil) {
self.value = value
}
}
然后我创建我的枚举:
enum Sample {
case Test(future: Future<String>)
}
实例化:
let enumTest = Sample.Test(future: Future())
后面的代码:
switch enumTest {
case let .Test(future):
future.value = "Foo"
}
稍后,您可以访问值:
switch enumTest {
case let .Test(future):
// it will print Optional("Foo")
print("\(future.value)")
}
这不是您应该滥用的东西,但在某些情况下它会有所帮助。
希望对您有所帮助。
不允许存储实例属性的枚举是一种设计选择。具有存储实例属性的枚举使其像一个结构(具有枚举超能力),但从类型的角度来看,现在枚举将像类型乘法器一样工作。基本考虑
enum Set1 {
case a
case b
case c
}
enum Times {
case x
case y
var k: Set1
}
这实际上意味着枚举 Times
允许我们对 Set1
和 Set2
中的元素进行任意组合,从而产生 6 种不同的情况,但是等等,我们知道实际上这是一个像 (Set1, Set2)
这样的元组类型的用途,其中 Times
可以声明为
typealias Times = (Set1, Set2)
我希望这是不允许前一种情况的合理理由。
也就是说,swift 枚举允许我们将任意 n 元组与每个 case 相关联,从而允许我们在函数式中声明所谓的可区分联合编程。如果您愿意,可以将其称为附加到案例的存储 属性。从类型的角度来看,它现在充当类型加法器。
enum Add {
case lhs(Set1)
case rhs(Set2)
}
我们现在有 5 个不同的案例。如果我们现在存储 2 元组:
enum AddTimes {
case lhs(Set1, Set2)
case rhs(Set3, Set4)
}
我们现在基本上有了乘法和 (Set1 * Set2 + Set3 * Set4)。 在模式匹配方面,这是一个非常强大的工具。
HOWEVER,在一些实际情况下,您实际上想要模拟枚举中存储的 属性 的形式。考虑一下:
public enum Service {
case registerNewUser(username: String, password: String, language: String)
case login(username: String, password: String, deviceTokenº: String?)
case logout(sessionToken: String)
case sendForgotPassword(email: String)
}
是一种定义 REST 端点的声明方式(在 Moya 等框架中) 当你想触发一个请求时,你会做类似
的事情MoyaProvider<Service>.request(.sendForgotPassword(email: "foo@foo.com"))
但现在假设您想区分生产服务器和测试服务器。如果在每种情况下都将服务器添加为另一个元组元素:
case forgotPassword(sessionToken: String, serverBaseURLString: String)
这将有一个错误的语义,因为您最初打算每个元组存储请求参数,但现在它存储一个服务器基址。
为了避免这样的事情,我们实际上可以通过以下方式参数化我们的类型。而不是将服务器定义为:
enum Server: String {
case production = "https://service.info"
case test = "http://test.service.info"
}
我们可以为每种情况定义不同的类型,例如:
public struct ProductionServer: ServerType {
public static var baseURLString: String { return "https://service.info" }
}
public struct TestServer: ServerType {
public static var baseURLString: String { return "http://test.service.info" }
}
public protocol ServerType {
static var baseURLString: String { get }
}
最后将我们的 ServiceType 参数化为
public enum Service<T> where T: ServerType {
case registerNewUser(username: String, password: String, language: String)
case login(username: String, password: String, deviceTokenº: String?)
case logout(sessionToken: String)
case sendForgotPassword(email: String)
var serverURL: URL {
return T.baseURL
}
}
public typealias ProdutionService = Service<ProductionServer>
public typealias TestService = Service<TestServer>