本章来介绍C#语言里的基本数据类型,以及字符串的一些操作方式。
基本数据类型
C#语言里的基本数据类型包括八种整数类型、两种用于科学计算的二进制浮点类型、一种用于金融计算的十进制浮点类型,一种布尔类型以及一种字符类型(包括其组合的string)。
整数类型
整数类型有以下几种,BCL是什么呢?在我的另一篇博客里介绍到了,其实就是CLR定义的适用于全平台语言的基本类型。
要使用C#自己的类型而不是BCL类型!要用string而不是String,这也解释了多年的困惑,为啥C#里有String和string,其实是相同的关键字,但是推荐使用C#自己的关键字。
浮点类型
浮点类型包括两种,其实和我们大多数接触过的语言都类似,float和double,值得注意的是:浮点数的精度由有效数位的个数来决定,而不是一个固定值。
decimal类型
decimal是128位精度的十进制浮点类型,虽然它的 精度比浮点型更高,但是范围比浮点型更小。
布尔类型
在条件表达式和条件语句中表示真或假,允许的值包括关键字true和false。由8位二进制位表示。
字符类型
字符类型就是char,由16位二进制位表示。需要注意的是其特殊字符的插入形式:
从上至下分别为:单引号、双引号、反斜杠以及Null。
从上至下分别为:Alert,退格、换页、换行、回车、水平制表符、垂直制表符、十六进制Unicode字符、十六进制Unicode字符(长度可变版本)、Unicode转义序列(用于创建代理项对)。
C#冷知识
1,在构造字符串这件事上,string类型是不可变的,而StringBuilder不仅可以提供和string类似的方法,例如:Append、AppendFormat、Insert、Remove、Relpace,还能保证方法可以直接修改StringBuilder本身的数据而不是返回一个新的字符串。
2,赋值为null表示变量已赋值,但无任何值变量已赋值,但无任何值,这和赋值为“”以及不赋值是有区别的,区别如下:
3,可以使用System.WriteLine()和System.Environment.NewLine()这两个方法来保证跨平台的换行符兼容性。
4,字面值表示源代码中的固定值,直接将值放到代码中叫硬编码,只要改值就需要重新编译代码,可以考虑将值通过配置文件读取,这样就不需要重新编译代码了。
5,默认情况下,输入小数会被当成double,输入整数会被当成int,如果整数超过int范围会被当成long。而C#还支持各种字面值的显示。
private static void Main() { Console.WriteLine(0b1000010); //二进制字面值 Console.WriteLine(0x0002AFE); //十六进制字面值 Console.WriteLine(6.02E23F); //指数计数法:E标识指数,F表示为浮点 Console.WriteLine($"0x{42:x}"); //将十进制格式化为十六进制:0x2a,如果是大写X,则为0x2A }
6,通过R格式化来保留值的精度:
private static void Main() { const double number = 1.618033988749895; var text = $"{number}"; var result = double.Parse(text); //精度丢失 System.Console.WriteLine($"{result == number}"); //返回false text = $"{number:R}"; result = double.Parse(text); //保留原值 System.Console.WriteLine($"{result == number}"); //返回true }
7,字符串驻留技术,虽然字符串不可变,赋值操作只会重新创建一个指向新内存的引用,所以改变str2的值操作并不会改变str1的值:
string str1 = "tml"; string str2 = str1; str2 = "hhh" //并不会改变str1的值
公共语言运行库通过维护一个表来存放字符串,该表称为拘留池,它包含程序中以编程方式声明或创建的每个唯一的字符串的一个引用。因此,具有特定值的字符串的实例在系统中只有一个:
string str1 = "CharlesChen"; string str2 = "CharlesChen"; System.Object.Equals(str1,str2) //返回True
当我们调用时候,返回值是True,按道理说是应该返回为false,str1和str2应该指向不同的内存空间才对。怎么会返回为true呢?这里就引入了"字符串驻留技术"。
其实这里CLR使用了字符串驻留技术,当CLR初始化时,会创建一个内部的散列表(Hash表),其中的键位字符串,值为指向托管堆中字符串的引用。刚开始,散列表为空,JIT编译器编译方法时,会在散列表中查找每一个文本字符串常量(这里是"CharlesChen"),首先会查找"CharlesChen",并且因为没找到,编译器会在托管堆中构造一个全新的指向"CharlesChen"的对象引用,然后将"abc"字符串和执行该对象的引用添加到散列表中去。
当string str2=“CharlesChen"时候,由于前面已经在散列表中加了该"CharlesChen"字符串,所以编译器不会执行任何分配内存空间的操作。首先编译器会在内部的散列表中查找"CharlesChen”,并且会找到,这样指向先前创建的String对象的引用就会被找到,并且这里Str2就指向找到的那个引用。因此Str1和Str2就指向了内存中同一个地址的引用。所以System.Ojbect.Equals(str1,str2)就返回为true了。
C#新知识
1,类型转换这件事:所有数值类型都能通过Parse()方法将对应的string类型转为数值类型。然而这种转换是不安全的!推荐使用TryParse方法
private static void Main() { const string tml1 = "1234568"; //变量未赋值,编译不能通过 var flag = int.TryParse(tml1, out var output); Console.WriteLine(output); }
从C#7.0开始, out参数可以使用内联的形式而不需要事先声明了! 除此之外,还推荐使用System.Convert()方法。
2,区分大小写的字符串比较:string.Compare(temp1,temp2)
,不区分大小写的字符串比较:string.Compare(temp1,temp2,true)
,