赞
踩
枚举,字面上理解,就是把东西一件件列出来。 在许多计算机语言中,枚举都是一种重要的数据结构。使用枚举可以使代码更简洁,语义性更强,更加健壮。 Swift语言也不例外。但和其他语言相比,Swift的枚举有一个关联值的概念。什么是关联值呢?关联值是枚举项存放的额外的数据,这些额外的数据可以使枚举描述更复杂的结构 ,实现更复杂的需求。
下面定义了一个枚举,其中的每个枚举项都具有关联值。
enum Progress
{
case begin(_ message:String)
case progress (_ percent:Double,_ message:String )
case end(_ message:String )
}
在上述枚举定义中,begin
项关联了一个类型为String
的值,枚举项表示开始,其关联值进一步表示是什么具体过程。 progress
项关联两个值,一个是百分比,一个是过程本身,枚举项表示进度,关联值则进一步表示是什么任务,到何种进度。有了关联值,我们就可以完整描述一个过程的具体进度了。
关联值看似平淡无奇,实则暗藏玄机。关联值既是Swift枚举不同于其他语言的独特之处,也是Swift枚举之所以强大的基础。
下面我们通过几个小例子简单说明。
Double
类型和String
类型的值放到同一个数组中这个问题好奇怪,为什么会有这样的问题?先不管为什么,先用枚举实现需求再说:
//定义枚举 enum DoubleAndString { case isdouble(_ value:Double) case isstring(_ value:String) } //定义枚举数组 let myarr:[DoubleAndString] = [ .isdouble(3.14), .isdouble(0.15926), .isstring("两个黄鹂鸣翠柳"), .isdouble(2.718), .isstring("一行白鹭上青天"), .isdouble(0.15926), ] //使用数组 myarr.forEach { switch $0 { case .isdouble (let value): // value 是Double类型,可直接使用 print("this is a double item \(value+1)") case .isstring (let value ): print("this is a string item \(value)") } }
可以看到,我们通过定义了一个DoubleAndString
枚举类型及其关联值,很轻易地将Double
类型和String
类型的值存放到一个数组中。
也许你会觉得这个很容易,用Any
数组不是也可以解决吗?如下使用Any
数组的代码,貌似比枚举还简洁一些:
let myarrA:[Any] = [
3.14,
0.15926,
"两个黄鹂鸣翠柳",
2.718,
"一行白鹭上青天",
0.15926,
true,
false
]
但是,它的主要问题是:用Any
数组没有语义。我们可以在这个数组中放Double
类型,也可以放String
类型,但因为是Any
类型,没有什么能够阻止我们放布尔类型,如true
和false
,或其他任何类型。另外一个问题是使用困难,因为没有类型,使用前必须进行类型判断和类型转换,如下代码:
myarrA.forEach { if $0 is Double { //$0 是Any类型,使用前需进行转换 let d = $0 as! Double print("double item: \(d+1)") } else { if $0 is String { print("string item: \($0)") } else { print("bad item: \($0)") } } }
Any
数组是一个巨大的盒子,进去容易出来难。因为它没有分门别类,找起来特别费劲,而且在使用前还得进行类型转换。枚举同样也是一个盒子,但已经按照各种case
分门别类,语义清晰,使用容易,孰优孰劣,一目了然。
相信更多的人会问,把Double
和String
放到一个数组中有什么实际意义吗?确实没有什么实际意义,但却可以揭示Swift枚举的本质:Swift枚举实际上是一个值的容器,我们可以通过它将不同类型的值(关联值)存放到同一个聚合类型(如数组中),并以高度语义化方式使用,这正是许多程序设计需要的非常有用的特性。
Java语言的枚举功能其实也比较强大,可以通过构造函数传入复杂的对象,但有个头痛的问题,枚举项一旦设定,就不能够增加新的项了,因而上述需求在Java语言中无法用枚举实现,只能用普通的类实现,这样就完全不能够享受枚举的便利性了。在Swift中,这样需求可以用枚举直接实现。
比如,我们要对CSDN的文章标签建模,内置标签只有下面几项;如下Swift代码就可以实现:
enum CsdnTag
{
case java
case swift
case mysql
}
但要实现增加自定义标签功能,我们还需要增加一个 custome
枚举项,该枚举项自带一个String类型的关联值,表示自定义的标签,另外,我们还需要增加了一个getTag()
函数,统一将内部标签和自定义标签转换为字符串:
enum CsdnTag { case java case swift case mysql case custome(_ tag:String) func getTag()->String { switch self { case .custome( let tag) :return tag default : return "\(self)" } } }
下面是使用枚举的代码:
//定义标签 var csdnTags:[CsdnTag] = [ .java, .swift, .mysql, //自定义三个标签 .custome("python"), .custome("redis"), .custome("vaadin") ] //使用标签 csdnTags.forEach { print ($0.getTag()) }
网络调用的简化的模型如下:
根据上面这两种情况,我们直接定义如下枚举:
enum NetResult
{
case success (_ data :String )
case error(_ errorCode:Int,_ errorString:String)
}
可以看到,建模过程是直截了当的,这是使用枚举的最大优势。
以NetResult
为基础,我们设计网络调用函数如下:
func fetchData(strUrl:String,resultHandler:(NetResult)->Void) { //网络调用处理 //网络调用完成,处理调用结果,假设调用成功 var isSuccess = false if isSuccess { resultHandler(.success("这里是网络调用返回的数据")) } else { resultHandler(.error(202,"非法地址")) } }
使用上述调用:
fetchData(strUrl:"www.blog.csdn")
{ result in
switch result
{
case .success(let data) :
print ("data is:\(data)")
case .error (let errorCode,let errorString):
print("Network error:errorCode=\(errorCode),errorString=\(errorString)")
}
}
读者可以思考一下不使用枚举时该如何处理网络调用?无论如何设计,一定都比上述枚举方法繁琐许多。
西方有一句谚语:拿起锤子,看什么都是钉子
。枚举就是Swift语言中的锤子。小伙伴们,赶紧回家拿起你的锤子吧…
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。