Golang 字符串
目录
- 1. Golang 字符串
- 1.1. 基础概念
- 1.2. 字符串编码
- 1.3. 遍历字符串
- 1.4. 类型转换
- 1.5. 总结
- 1.6. String Concatenation (字符串连接)
- 1.6.1. Using the `+` operator
- 1.6.2. Using the `+=` operator
- 1.6.3. Using the Join method
- 1.6.4. Using Sprintf method
- 1.6.5. Using Go string Builder method
- 1.6.6. Using the Bytes buffer method
- 1.7. String Comparison
- 1.7.1. Using Comparison operators
- 1.7.2. Using Compare method
- 1.7.3. Using strings EqualFold
- 1.8. String Manipulation and utility methods
- 1.8.1. ToLower, ToUpper and Title functions
- 1.8.2. Contains and ContainsAny functions
- 1.8.3. Split and SplitAfter functions
- 1.8.4. Repeat and Fields functions
1. Golang 字符串
1.1. 基础概念
ASCII 是英文"American Standard Code for Information Interchange"的缩写,中文译为美国信息交换标准代码,它是由美国国家标准学会 (ANSI) 制定的单字节字符编码方案,它使用单个字节 (byte) 的二进制数来编码一个字符。
Unicode 编码规范为世界上现存的所有自然语言中的每一个字符,都设定了一个唯一的二进制编码。它以 ASCII 编码集为出发点,并突破了 ASCII 只能对拉丁字母进行编码的限制。Unicode 编码规范通常使用十六进制表示法来表示 Unicode 代码的整数值,并提供了三种不同的编码格式,即:UTF-8、UTF-16 和 UTF-32。
UTF-8 以 8 个比特(一个字节)作为一个编码单元,它是一种可变宽的编码方案,它会用一个或多个字节的二进制数来表示某个字符,最多使用四个字节。对于一个英文字符,它仅用一个字节的二进制数就可以表示,而对于一个中文字符,它需要使用三个字节才能够表示。rune
是 Go 语言特有的一个基本数据类型,它的一个值就代表一个 Unicode 字符,比如’吕’、‘M’。一个 rune
类型的值会由四个字节宽度的空间来存储,它的存储空间总是能够存下一个 UTF-8 编码值。
1.2. 字符串编码
一个 rune
类型的值在底层其实就是一个 UTF-8 编码值,前者是(便于我们人类理解的)外部展现,后者是(便于计算机系统理解的)内在表达,请看下面代码:
func main() {str := "Go 爱好者"fmt.Printf("The string: %q\n", str)fmt.Printf("runes(char): %q\n", []rune(str)) //['G' 'o' '爱' '好' '者']fmt.Printf("runes(hex): %x\n", []rune(str)) //[47 6f 7231 597d 8005]fmt.Printf("bytes(hex): [% x]\n", []byte(str)) //[47 6f e7 88 b1 e5 a5 bd e8 80 85]
}
对于第 3 行输出,前面解释的比较清楚,就不赘述。对于第 4 行输出,就是通过 UTF-8 编码,3 个字节的 16 进制展现。第 5 行输出,把每个字符的 UTF-8 编码值都拆成相应的字节序列。
一句话总结一下:一个 string 类型的值在底层就是一个能够表达若干个 UTF-8 编码值的字节序列。
1.3. 遍历字符串
range 遍历:
func main() {str := "Go 爱好者"fmt.Printf("range 遍历:\n")for i, c := range str {fmt.Printf("%d: %q [% x]\n", i, c, []byte(string(c)))}fmt.Printf("for 遍历:\n")for i := 0; i < len(str); i++ {fmt.Printf("%d: [%c] [%x]\n", i, str[i], str[i])}
}
输出如下:
range 遍历:
0: 'G' [47]
1: 'o' [6f]
2: ' ' [20]
3: '爱' [e7 88 b1]
6: '好' [e5 a5 bd]
9: '者' [e8 80 85]
for 遍历:
0: [G] [47]
1: [o] [6f]
2: [ ] [20]
3: [ç] [e7]
4: [ˆ] [88]
5: [±] [b1]
6: [å] [e5]
7: [¥] [a5]
8: [½] [bd]
9: [è] [e8]
10: [€] [80]
11: [
] [85]
由此可以看出,通过 range
方式的遍历,是以 rune
为单位,但是相邻字符的索引值并不一定是连续的;通过 for
方式的遍历,是以 byte 为单位。
1.4. 类型转换
字符串是不能直接修改的,如果需要修改,需要转换为可变类型 ([]rune
和 []bype
), 待修改完后再转换回来。但不管如何转换,都需要重新分配内存,并复制数据。
func main() {str := "hello, world!"bs := []byte(str) // string 转 bytestr2 := string(bs) // byte 转 stringrs := []rune(str) // string 转 runestr3 := string(rs) // rune 转 string
}
前面已经讲解 string
、rune
和 byte
的区别和联系,这里再理解他们的转换,是不是就轻松很多了呢。
1.5. 总结
Go 语言的代码是由 Unicode 字符组成的,它们都必须由 Unicode 编码规范中的 UTF-8 编码格式进行编码并存储,Unicode 编码规范中的编码格式定义的是:字符与字节序列之间的转换方式。其中的 UTF-8 是一种可变宽的编码方案,它会用一个或多个字节的二进制数来表示某个字符,最多使用四个字节。Go 语言中的一个 string
类型值会由若干个 Unicode 字符组成,每个 Unicode 字符都可以由一个 rune
类型的值来承载。这些字符在底层都会被转换为 UTF-8 编码值,而这些 UTF-8 编码值又会以字节序列的形式表达和存储。因此,一个 string
类型的值在底层就是一个能够表达若干个 UTF-8 编码值的字节序列。对于通过 for range
方式遍历字符串,会先把被遍历的字符串值拆成一个字节序列,然后再试图找出这个字节序列中包含的每一个 UTF-8 编码值,或者说每一个 Unicode 字符。相邻的 Unicode 字符的索引值并不一定是连续的,这取决于前一个 Unicode 字符是否为单字节字符,一旦我们清楚了这些内在机制就不会再困惑了。对于 Go 语言来说,Unicode 编码规范和 UTF-8 编码格式算是基础之一,我们应该了解到它们对 Go 语言的重要性,这对于正确理解 Go 语言中的相关数据类型以及日后的相关程序编写都会很有好处。
1.6. String Concatenation (字符串连接)
1.6.1. Using the +
operator
package mainimport ("fmt"
)func main() {s1 := "Go"s2 := "Programming"s3 := "Language"s := s1 + s2 + s3fmt.Println(s)
}
$ go run concatenate.goGoProgrammingLanguage
1.6.2. Using the +=
operator
p := "Story"
p += "Book"
fmt.Println(p)
go run concatenate.goStoryBook
1.6.3. Using the Join method
q := []string{"meetgor.com", "tags", "golang", "string"}
r := strings.Join(q, "/")
fmt.Println(r)
go run concatenate.gomeetgor.com/tags/golang/string
1.6.4. Using Sprintf method
// Using Sprintf function to format stringsname := "peter"
domain := "telecom"
service := "ceo"email := fmt.Sprintf("%s.%s@%s.com", service, name, domain)
fmt.Println(email)
go run concatenate.goceo.peter@telecom.com
1.6.5. Using Go string Builder method
package mainimport ("fmt""strings"
)func main() {// Using Builder functionc := []string{"j", "a", "v", "a"}var builder strings.Builderfor _, item := range c {builder.WriteString(item)}fmt.Println("builder = ", builder.String())b := []byte{'s', 'c', 'r', 'i', 'p', 't'}for _, item := range b {builder.WriteByte(item)}fmt.Println("builder = ", builder.String())builder.WriteRune('s')fmt.Println("builder = ", builder.String())fmt.Println("builder = ", builder)
}
go run concatenate.gobuilder = java
builder = javascript
builder = javascripts
builder = {0xc000088dd8 [106 97 118 97 115 99 114 105 112 116 115]}
1.6.6. Using the Bytes buffer method
package mainimport ("fmt""bytes"
)func main() {// Using bytes buffer methodvar buf bytes.Bufferfor i := 0; i < 2; i++ {buf.WriteString("ja")}fmt.Println(buf.String())buf.WriteByte('r')fmt.Println(buf.String())k := []rune{'s', 'c', 'r', 'i', 'p', 't'}for _, item := range k {buf.WriteRune(item)}fmt.Println(buf.String())
}
go run concatenate.gojaja
jajar
jajarscript
{[106 97 106 97 114 115 99 114 105 112 116] 0 0}
1.7. String Comparison
1.7.1. Using Comparison operators
package mainimport "fmt"func main() {s1 := "gopher"s2 := "Gopher"s3 := "gopher"isEqual := s1 == s2//"gopher" is not same as "Gopher" and hence `false`fmt.Printf("S1 and S2 are Equal? %t \n", isEqual)fmt.Println(s1 == s2)// "gopher" is not equal to "Gopher" and hence `true`fmt.Println(s1 != s2)// "Gopher" comes first lexicographically than "gopher" so return true // G -> 71 in ASCII and g -> 103fmt.Println(s2 < s3)fmt.Println(s2 <= s3)// "Gopher" is not greater than "gopher" as `G` comes first in ASCII table// So G value is less than g i.e. 71 > 103 which is falsefmt.Println(s2 > s3)fmt.Println(s2 >= s3)}
go run comparison.goS1 and S2 are Equal? false
false
true
true
true
false
false
1.7.2. Using Compare method
package mainimport("fmt""strings"
)func main() {s1 := "gopher"s2 := "Gopher"s3 := "gopher"fmt.Println(strings.Compare(s1, s2))fmt.Println(strings.Compare(s1, s3))fmt.Println(strings.Compare(s2, s3))
}
go run comparison.go1
0
-1
1.7.3. Using strings EqualFold
package mainimport("fmt""strings"
)func main() {s1 := "gopher"s2 := "Gopher"s3 := "gophy"fmt.Println(strings.EqualFold(s1, s2))fmt.Println(strings.EqualFold(s1, s3))fmt.Println(strings.EqualFold(s2, s3))
}
go run comparison.gotrue
false
false
1.8. String Manipulation and utility methods
1.8.1. ToLower, ToUpper and Title functions
package mainimport ("fmt""strings""golang.org/x/text/cases""golang.org/x/text/language"
)func main() {s1 := "Ubuntu 22"s2 := "meet"s3 := "IND"fmt.Println(strings.ToLower(s1))fmt.Println(strings.ToLower(s2))fmt.Println(strings.ToLower(s3))fmt.Printf("\n")fmt.Println(strings.ToUpper(s1))fmt.Println(strings.ToUpper(s2))fmt.Println(strings.ToUpper(s3))fmt.Printf("\n")cases := cases.Title(language.English)fmt.Println(cases.String(s1))fmt.Println(cases.String(s2))fmt.Println(cases.String(s3))
}
# 100-days-of-golang/scripts/stringsgo mod init
go get golang.org/x/text/cases
go get golang.org/x/text/languagego run utility.go
ubuntu 22
meet
indUBUNTU 22
MEET
INDUbuntu 22
Meet
Ind
1.8.2. Contains and ContainsAny functions
package mainimport ("fmt""strings"
)func main() {str := "javascript"substr := "script"s := "python"fmt.Println(strings.Contains(str, substr))fmt.Println(strings.Contains(str, s))fmt.Println(strings.ContainsAny(str, "joke"))fmt.Println(strings.ContainsAny(str, "xyz"))fmt.Println(strings.ContainsAny(str, ""))
}
$ go run main.gotrue
false
true
false
false
1.8.3. Split and SplitAfter functions
package mainimport ("fmt""strings"
)func main() {// Split method for splitting string into slice of stringlink := "meetgor.com/blog/golang/strings"fmt.Println(strings.Split(link, "/"))fmt.Println(strings.SplitAfter(link, "/"))// SplitAfter method for splitting string into slice of string with the patterndata := "200kms50kms120kms"fmt.Println(strings.Split(data, "kms"))fmt.Println(strings.SplitAfter(data, "kms"))
}
go run utility.go[meetgor.com blog golang strings]
[meetgor.com/ blog/ golang/ strings]
[200 50 120 ]
[200kms 50kms 120kms ]
1.8.4. Repeat and Fields functions
package mainimport ("fmt""strings"
)func main() {// Repeat method for creating strings with given string and integerpattern := "OK"fmt.Println(strings.Repeat(pattern, 3))
}
go run utility.goOKOKOK
package mainimport ("fmt""strings"
)func main() {// Fields method for extracting string from the given string with white space as delimiterstext := "Python is a prgramming language. Go is not"text_data := strings.Fields(text)fmt.Println(text_data)for _, d := range text_data {fmt.Println("data = ", d)}
}
go run utility.go[Python is a prgramming language. Go is not]
data = Python
data = is
data = a
data = prgramming
data = language.
data = Go
data = is
data = not
相关文章:
Golang 字符串
目录 1. Golang 字符串1.1. 基础概念1.2. 字符串编码1.3. 遍历字符串1.4. 类型转换1.5. 总结1.6. String Concatenation (字符串连接)1.6.1. Using the operator1.6.2. Using the operator1.6.3. Using the Join method1.6.4. Using Sprintf method1.6.5. Using Go string Bu…...
python应用中使用了multiprocessing多进程,使用pyinstaller打包出来的程序可能产生多个窗口
问题现象 我用pyside(类似pyqt)开发了一个应用程序。直接使用pycharm运行,一切都正常。但当我使用pyinstaller将它打包之后,再去运运行,发现窗口总是产生多个。 问题分析 直接运行没有问题,那么问题肯定…...
数据结构与算法——13.队列的拓展
这篇文章主要讲一下双端队列,优先队列,阻塞队列等队列的拓展内容。 目录 1.队列拓展概述 2.双端队列的链表实现 3.双端队列的数组实现 4.优先队列无序数组实现 5.阻塞队列 6.总结 1.队列拓展概述 首先来看一张图,来大致了解一下他们的…...
机器学习入门教学——损失函数(交叉熵法)
1、前言 我们在训练神经网络时,最常用到的方法就是梯度下降法。在了解梯度下降法前,我们需要了解什么是损失(代价)函数。所谓求的梯度,就是损失函数的梯度。如果不知道什么是梯度下降的,可以看一下这篇文章:机器学习入…...
pytest一些常见的插件
Pytest拥有丰富的插件架构,超过800个以上的外部插件和活跃的社区,在PyPI项目中以“ pytest- *”为标识。 本篇将列举github标星超过两百的一些插件进行实战演示。 插件库地址:http://plugincompat.herokuapp.com/ 1、pytest-html࿱…...
基于51单片机多路DTH11温湿度检测控制系统
一、系统方案 1、本设计采用51单片机作为主控器。 2、DHT11采集温度度,支持3路温度度,液晶1602显示。 3、按键设置报警阀值。 4、系统声光报警。 二、硬件设计 原理图如下: 三、单片机软件设计 1、首先是系统初始化 //初始化LCD*********…...
宝塔重装注意事项
欢迎关注我的公众号:夜说猫,让一个贫穷的程序员不靠打代码也能吃饭~ 前言 宝塔8.0版本,宝塔卸载重装,或者重装Linux系统后重新安装宝塔也适用。 不能上来直接就执行安装宝塔脚本,除非之前没有安装过宝塔。 步骤 1、…...
【MySQL】 MySQL的增删改查(进阶)--壹
文章目录 🛫数据库约束🌴约束类型🎋NOT NULL约束🎍UNIQUE:唯一约束🌳DEFAULT:默认值约束🎄PRIMARY KEY:主键约束🍀FOREIGN KEY:外键约束…...
Map<K,V>的使用和List学习
Map Map是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关。对于静态类型的查找来说,一般直接遍历或者用二分查找【不会对区间进行插入和删除操作】 而在现实生活中的查找比如: 根据姓名查询考试成绩通讯录…...
Flask实现Web服务调用Python程序
Flask实现Web服务调用Python程序_flask调用python程序_小白白程序员的博客-CSDN博客 【小沐学Python】Python实现Web服务器(Flask入门)_python flask web开发_爱看书的小沐的博客-CSDN博客...
步步为营,如何将GOlang引用库的安全漏洞修干净
文章目录 引场景构建第一步、直接引用的第三方库升级修复策略1.确认是否为直接引用的第三方库2.找到需要升级的版本是否为release版本 第二步、间接引用的第三方库升级修复策略那么问题来了,我们这么间接引用库的对应的直接引用库是哪个呢? (…...
as-if-serial与happens-before原则详解
文章目录 前言详解解决多线程下的问题 Happens-before原则总结as-if-serial语义happens-before的例子 前言 "as-if-serial"原则是Java内存模型中的一个重要概念。该规则规定:不管怎么重排序(编译期间的重排序,指令级并行的重排序&…...
基于Yolov8的工业小目标缺陷检测(2):动态蛇形卷积(Dynamic Snake Convolution),实现暴力涨点 | ICCV2023
目录 1.工业油污数据集介绍 1.1 小目标定义 1.2 难点 1.3 工业缺陷检测算法介绍 1.3.1 YOLOv8...
ARM64汇编基础
ARM64汇编基础 主要内容 到目前为止,大部分的移动设备都是64位的arm架构,一直想抽个时间系统学习下,这个周末就专门来学习下。毕竟两天的时间,也只是简单的入门了解下,为后续工作和学习打下基础。 本次学习的主要内容…...
Nodejs 第十六章(ffmpeg)
FFmpeg 是一个开源的跨平台多媒体处理工具,可以用于处理音频、视频和多媒体流。它提供了一组强大的命令行工具和库,可以进行视频转码、视频剪辑、音频提取、音视频合并、流媒体传输等操作。 FFmpeg 的主要功能和特性: 格式转换:…...
k8s集群部署es
集群内创建需要用到存储,此处举例使用腾讯云cfs共享存储 内存limits限制不需要加,否则会经常内存溢出导致es集群故障 apiVersion: apps/v1 kind: StatefulSet metadata:name: es7-clusternamespace: elasticsearch spec:serviceName: es-clusterreplica…...
学习记忆——宫殿篇——记忆宫殿——记忆桩——火车+外院+客厅+卧室
护板 警示灯 烟筒 采集箱 司炉室 桥 电线杆 棚顶 车厢 护栏 植物 石阶 水泥台 竹门 树干 躺椅 柱子 墙 池 洞 方灯 枕头 树 浴池 墙 射灯 藤条 浴巾框 耳环 窗户 灯 沙发 壁炉 吊灯 兵马俑 门 石佛 沙发椅 圆木 弧形木箱盖 床 窗帘 画板 纸伞 花 沙发背 颜料 抽屉...
QT用户登录注册,数据库实现
登录窗口头文件 #ifndef LOGINUI_H #define LOGINUI_H#include <QWidget> #include <QLineEdit> #include <QPushButton> #include <QLabel> #include <QMessageBox>#include <QSqlDatabase> //数据库管理类 #include <QSqlQuery> …...
GEE学习总结(9)——像元二分法计算月度植被覆盖度(MODIS)
像元二分法计算植被覆盖度 通过MODIS的NDVI数据集MOD13Q1和像元二分法计算植被覆盖度 var multi_NDVI ee.ImageCollection(MODIS/006/MOD13Q1).filterDate(2015-06-01, 2016-09-01).select(NDVI).max().divide(10000).clip(geometry);var ndviVis {min: 0.0,max: 1,palette…...
RabbitMQ用户命令_策略_日志
RabbitMQ相关安装 Centos离线安装RabbitMQ并开启MQTT Docker安装rabbitMQ RabbitMQ集群搭建和测试总结_亲测 Docker安装RabbitMQ集群_亲测成功 RabbitMQ创建管理员命令 #查看当前用户命令: rabbitmqctl list_users#创建用户和密码 rabbitmqctl add_user admin…...
停车场系统、智慧城市停车、智慧社区、物业管理、新能源充电、人脸门禁 uniapp 系统源码
1. 智慧停车 支持模式 封闭性单个停车场路边停车(车位级管理)大小场(场中场),多场子并行或嵌套 所有者模式 统一平台管理总平台下子账号(区域代理)自建场地资源,自行维护数据总平台下子账号(区域代理)再分配和单个停车场管理人员(物业管理/维保/保安/财务…...
Linux磁盘管理
物理设备的命名规则 在linux系统中一切都是文件,硬件设备也不例外。即然是文件,就必须有文件名称。系统内核中的udev设备管理器会自动把硬件名称规范起来,目的是让用户通过设备文件的名字可以看出设备大致的属性以及分区信息等;在…...
vue学习之vue cli创建项目
安装 node.js https://nodejs.org/en 安装 vue cli npm install -g @vue/cli --registry=https://registry.npm.taobao.org创建项目 执行创建命令,回车vue create vue-cli-learning选择 “Manually select features”,回车 “空格” 关闭 Linter / Formatter 选项,回车...
K8S:Pod容器中的存储方式及PV、PVC
文章目录 Pod容器中的存储方式一.emptyDir存储卷1.emptyDir存储卷概念2.emptyDir存储卷示例 二.hostPath存储卷1.hostPath存储卷概念2.hostPath存储卷示例 三.nfs共享存储卷1.nfs共享存储卷示例 四.PV和PVC1.PV、PVC概念2.PVC 的使用逻辑及数据流向3.storageclass插…...
uni-app跳转到另一个app
第一步: 首先要知道 app的包名 获取方式如下 第二步: 在第一个 demo1 app 一个页面中需要一个按钮去跳转 方法如下 <template><view class"content"><button click"tz">跳转</button></view> </…...
如何通过一键导出导入数据实现批量重命名文件名称
在日常办公中,我们经常需要对大量的文件进行重命名,以便更好地管理和查找文件。而且,有时候我们还需要将文件名称翻译成其他语言,以适应不同的工作需求。如何高效地完成这项任务呢?接下来,我将介绍一种方法…...
CTF —— 网络安全大赛(这不比王者好玩吗?)
前言 随着大数据、人工智能的发展,人们步入了新的时代,逐渐走上科技的巅峰。 \ ⚔科技是一把双刃剑,网络安全不容忽视,人们的隐私在大数据面前暴露无遗,账户被盗、资金损失、网络诈骗、隐私泄露,种种迹象…...
3D模型转换工具HOOPS Exchange如何实现OBJ格式轻量化?
什么是OBJ模型轻量化? OBJ格式是一种常用的三维模型文件格式,通常包含模型的顶点、法线、纹理坐标等信息,但有时候这些信息可能会使模型文件变得较大,不利于网络传输、加载和运行。 OBJ(Object)模型轻量化…...
命令模式-
定义:又叫动作模式或事务模式。指的是将一个请求封装成一个对象,使发出请求的责任和执行请求的责任分割开,然后可以使用不同的请求把客户端参数化,这样可以使得两者之间通过命令对象进行沟通,从而方便将命令对象进行储…...
进程的管理
#include <unistd.h> void _exit(int status); #include <stdlib.h> void _Exit(int status); status参数:是进程退出时的状态信息,父进程在回收子进程资源的时候可以获取到 #include<stdio.h> #include<stdlib.h> #includ…...
cn域名注册/网络推广优化品牌公司
关于 http://openresty.org/cn/about.html 这个开源 Web 平台主要由章亦春(agentzh)维护。在 2011 年之前曾由淘宝网赞助,在后来的 2012 ~ 2016 年间主要由美国的 CloudFlare 公司 提供支持。目前,OpenResty 主要由 OpenResty 软件…...
苏州做网站建设公司/最新的全国疫情
一、HTTP的缺点 到目前为止,我们已经介绍完HTTP,它具有相当优秀和方便的一面,但它也有不足之处,HTTP 主要有这些不足,例举如下。 通信使用明文(不加密),内容可能会被窃听 不验证通…...
怎么看网站建设有多久/营销技巧培训ppt
07.JAVA中,什么是构造方法?什么是构造方法重载?什么是复制构造方法? 当新对象被创建的时候,构造方法会被调用。每一个类都有构造方法。在程序员没有给类提供构造方法的情况下,JAVA编译器会为这个类创建一个…...
苏州做公司邮箱企业网站/绍兴百度seo
轨道交通运营与管理专业的工资多少?现在的高考成绩都已经公布,志愿填报成为考生现在的重要之事,选择一个就业前景好,而且工资高的专业,是每个考生和家长希望的事。现在,随着轨道交通行业的快速发展,市场对…...
用asp做宠物网站页面/网站怎么接广告
用python将图片转成字符串 下面是原始图片;代码: from PIL importImage ascii_char list("$B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_~<>i!lI;:,\"^.") WIDTH 85 #字符画的宽 HEIGHT 25 #字符画的高 #将256灰度映…...
做律师咨询网站/山东疫情最新情况
简介 自.NET 4.5发布已经过了差不多1年了。但是随着最近微软大多数的发布,与.NET开发者交流的问题显示,开发者仅知道一到两个特性,其他的特性仅仅停留在MSDN并以简单的文档形式存在着。 比如说,当你问一个.NET开发者.NET框架内核中…...