C#中的数据类型分为两种:一种是值类型,一种是引用类型。所以每个对象要么是值类型的实例,要么是引用类型的实例。
引用类型和值类型都继承自System.Object类。不同的是,几乎所有的引用类型都直接从System.Object继承,而值类型则继承其子类,即直接继承System.ValueType。
作为所有类型的基类,System.Object提供了一组方法,这些方法在所有类型中都能找到,其中包含toString方法及clone等方法。System.ValueType直接继承System.Object,即System.ValueType本身是一个类类型,而不是值类型;System.ValueType没有添加任何成员,但覆盖了所继承的一些方法,使其更适合于值类型。
值类型与引用类型的区别:
1. 值类型的数据存储在内存的栈中;引用类型的数据存储在内存的堆中,而内存单元中只存放堆中对象的地址。
2. 值类型存取速度快,引用类型存取速度慢。
3. 值类型表示实际数据,引用类型表示指向存储在内存堆中的数据的指针或引用
4. 值类型继承自System.ValueType,引用类型继承自System.Object
5. 栈的内存分配是自动释放;而堆在.NET中会有GC(Garbage Collection 垃圾回收器)来释放
6. 值类型的变量直接存放实际的数据,而引用类型的变量存放的则是数据的地址,即对象的引用。
7. 值类型变量直接把变量的值保存在堆栈中,引用类型的变量把实际数据的地址保存在堆栈中,而实际数据则保存在堆中。注意,堆和堆栈是两个不同的概念,在内存中的存储位置也不相同,堆一般用于存储可变长度的数据,如字符串类型;而堆栈则用于存储固定长度的数据,如整型类型的数据int(每个int变量占用四个字节)。由数据存储的位置可以得知,当把一个值变量赋给另一个值变量时,会在堆栈中保存两个完全相同的值;而把一个引用变量赋给另一个引用变量,则会在堆栈中保存对同一个堆位置的两个引用,即在堆栈中保存的是同一个堆的地址。在进行数据操作时,对于值类型,由于每个变量都有自己的值,因此对一个变量的操作不会影响到其它变量;对于引用类型的变量,对一个变量的数据进行操作就是对这个变量在堆中的数据进行操作,如果两个引用类型的变量引用同一个对象,实际含义就是它们在堆栈中保存的堆的地址相同,因此对一个变量的操作就会影响到引用同一个对象的另一个变量。
● 值类型变量声明后,不管是否已经赋值,编译器为其分配内存。
● 引用类型当声明一个类时,只在栈中分配一小片内存用于容纳一个地址,而此时并没有为其分配堆上的内存空间。当使用 new 创建一个类的实例时,分配堆上的空间,并把堆上空间的地址保存到栈上分配的小片空间中。
● 值类型的实例通常是在线程栈上分配的(静态分配),但是在某些情形下可以存储在堆中。
● 引用类型的对象总是在进程堆中分配(动态分配)。
值类型有:数值类型(整型、浮点型、十进制型)、布尔类型、枚举类型、结构类型
引用类型有:类(基类object、字符串类string、自定义类class)、接口、数组
(一)数值类型
C#中数值类型又分为整型、浮点型、十进制型三种。
1、整型
不同的整数类型会占用大小不同的内存空间(至多64位),所能表达的数字范围也不尽相同,我们只有知道了每种类型的取值范围,才能正确使用它们。
某些类型前面的“u”是unsigned (无符号)的缩写,表示不能在这些类型的变量中存储负数。
2、浮点型
浮点型分为单精度浮点数(float)和双精度浮点数(double)。
3、十进制型
十进制类型(关键字位decimal)表示128位的数据类型。与浮点数相比。它具有更高的精度和更小的范围,适用于财务和货币的计算。
如想实数被视为十进制类型,后缀加m或M,如:decimal money = 500.3m;
(二)布尔类型
布尔类型(bool)用于储存布尔值true(对)和false(错)。可以直接把布尔值赋给bool变量,也可以将计算结果为布尔类型的表达式赋给bool变量。
示例:
//例1 bool mybool; int a = 10; int b = 20; mybool = a > b; //如果大于返回true,如果小于返回false; 此时mybool为false mybool = a < b; //如果大于返回true,如果小于返回false; 此时mybool为true mybool = a >= b; //如果大于等于返回true,否则false; 此时mybool为false mybool = a <= b; //如果小于等于返回true,否则false; 此时mybool为true mybool = a != b; //如果不等于返回true,否则false; 不等于符号是!=; 此时mybool为true mybool = a == b; //如果等于返回true,否则false; 等于符号是==; 此时mybool为false
//例2 布尔运算符 bool a = true; bool b = false; Console.WriteLine("若 bool a = true; bool b = false;那么 a|| b 返回结果则是:{0}", a || b); // 或【|】、【||】,有一个true,结果true ,否则false ;运算符前一个操作数为true值时,不再执行后一个操作数的内容; Console.WriteLine("若 bool a = true; bool b = false;那么 a && b 返回结果则是:{0}", a && b); // 与【&】、【&&】;同时为true时,结果为true;否则false ; Console.WriteLine("若 bool a = true; bool b = false;那么 a = !b 返回结果则是:{0}", !b); // !非(取反)b 为false时,则true; Console.WriteLine("若 bool a = true; bool b = false;那么 a ^ b 返回结果则是:{0}", a ^ b); // ^有且只有一个;有且只有一个时结果true,否则false; Console.ReadKey();
例2运行结果:
(三)字符串类型
string(字符串)类型是System.String的别名,表示由零个或多个Unicode字符组成的不可变序列。可使用“[ n ]”运算符访问string中的每个字符,其中n为下标。
string类型:在c#语言中,字符串(string)类型是用一对双引号引起来的字符;string类型具有不可变性。
string str = "Hello World!"; // 声明并初始化 str = "Hi"; //变量重新赋值 Console.WriteLine(str); Console.ReadKey();
在“Hi”字符串赋给变量str之前,内存首先会重新初始化一块区域,并把该区域的值初始化为Hi,之后,这块内存的地址将赋给变量str,而原来存放 "Hello World!"的内存区域则不会被修改。所以string类型具有不可变性。
string类型为引用类型,string变量本身存储在栈中,而变量所指向的值则存储在托管堆里。
(四)枚举类型
枚举类型属于值类型,它用于声明一组命名了的常数。枚举类型使用enum关键字来完成声明。
代码示例:
//引用命名空间 (这里是引用命名空间的位置) using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp //命名空间或项目名称 { //如果希望得到一个固定集合的值,就采用枚举; //在此处声明枚举,它与结构的作用很类似,所以位置一样,但同时,枚举也可以在结构中被调用; //枚举的声明位置:将枚举声明到命名空间的下面,类的外面,表示这个命名空间下,所有的类都可以使用。 //枚举的声明方法:enum 枚举名(enum前可以加上修饰符如public公共的公开的,在哪都可以访问) public enum week //定义枚举星期 { //默认情况下,每个值都会根据定义的顺序从0开始,自动赋予每个值一个整型。当然,也可以人为指定一个枚举的基础值。 星期一, //0 星期二, //1 星期三, //2 星期四, //3 星期五, //4 星期六, //5 星期日, //6 } public enum sex //定义枚举性别 { 男 = 1, //人为指定一个枚举的基础值 女 = 5 //人为指定一个枚举的基础值 } class Program //Program类 { static void Main(string[] args) { //枚举就是一个变量类型,int,double,string,decimal (只是枚举的声明、赋值、使用方法跟那些普通的变量类型不一样) sex gender = sex.男; //(枚举名 自定义的变量名=枚举名.值) Console.WriteLine(gender); //输出,输出结果是男 //枚举与int类型的相互转换 week workday = week.星期一; Console.WriteLine((int)workday); //(int)实现将枚举转换为整型,输出结果是0 int num = 5; Console.WriteLine((sex)num); //(枚举名)实现将整型转换为枚举,输出结果是女 //枚举转换为字符串 Console.WriteLine(workday); //输出过程的时候,内部其实已经实现了一个转换,就是把枚举值转换串string类型的,但也可以表面上去指定,也可以不指定。 Console.WriteLine(workday.ToString()); //例表面上去指定,不能用(string)如: Console.WriteLine((string)workday);只能用workday.ToString();或Convert.ToString(workday); //将字符串转换为枚举值 //将字符串转换成枚举类型代码:(要转换的枚举类型)Enum.Parse(typeof(要转换的枚举类型), "要转换的字符串") string str = "星期六"; Console.WriteLine((week)Enum.Parse(typeof(week), str));//如果需要转换的字符串不是数字,而且枚举值中不包含有该值,就会报异常;例:str = "星期八";报异常;这条语句输出结果为星期六 string str1 = "123456789"; Console.WriteLine((week)Enum.Parse(typeof(week), str1));//如果需要转换的字符串是数字,而且枚举值中不包含有该值,不报异常。这条语句输出结果为123456789 Console.ReadKey(); } } }
默认情况下,枚举中的每个元素都是int类型,用户可通过使用冒号来制定一种全新的数字类型。方法如下所示:
enum Gender:byte { Male, //男 Female,//女 }
(五)、结构体类型
C#中的结构体是一种值类型,结构体与类有着几乎相同的语法。
定义结构的语法如下:
访问修饰符 struct 自定义的结构名称
{
//定义结构成员
}
注意定义好一个结构后,就可以直接声明相应的变量了,声明好变量后,通过变量名.成员名 来访问结构的成员。
代码示例:
//定义结构示例: public struct UserInfo //定义一个叫UserInfo的结构 { //以下是结构的成员 public string name; //姓名 public char sex; //性别 public int age; //年龄 } //调用结构示例: UserInfo user; user.name = "张三"; user.sex= '男'; user.age = 18;
(六)、数组类型
数组是一种引用类型,它代表相同类型元素的集合。
语法:type[ ] array = new type[ arrayCount ];
这里的type为数组元素的类型,即可以是值类型,也可以是引用类型。声明数组时还应注意方括号[ ]应紧跟类型tpye后,变量名array前。访问数组的元素可以用array[index]这里的index是指数组的下标,下标是从0开始,最大不能超过数组的长度减去1。如果下标超出这个范围,程序会在运行时出现IndexOutOfRangeException异常。
代码示例:
//引用命名空间 (这里是引用命名空间的位置) using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp //命名空间或项目名称 { class Program //Program类 { static void Main(string[] args) { //一维数组的格式: int[] arr_1 = new int[] { 3, 2, 1 }; //第一种声明方式 int[] arr_2 = { 3, 2, 1 }; //第二种方式声明一个数组,直接对数组赋初值。 int[] arr_3; //第三种声明方式 arr_3 = new int[] { 3, 2, 1 }; //二维数组的格式: int[,] arr_4 = new int[,] { { 1, 2, 3 }, { 1, 2, 3 } }; //不定长 int[,] arr_5 = new int[2, 2] { { 1, 2, }, { 1, 2, } }; //定长 //三维数组的格式: int[,,] arr_6 = new int[4, 2, 3]; double zf = 0; //定义变量总分,初始值为0; double[] cj; //数组的定义,定义一个名为cj的数组,数组有多少个元素暂时不知; cj = new double[10]; //数组的实例化,为这个数组分配10个存储空间(能存储10个double类型的的内存空间); Console.WriteLine("请输入10名学生的单科成绩:"); for (int i = 0; i < cj.Length; i++) //对数组元素进行赋值 { cj[i] = double.Parse(Console.ReadLine()); //数组通过下标来访问,接收用户输入的成绩,Console.ReadLine();用于接受用户的输入,输入类型默认为字符串string类型,所以需要double.Parse类型转换 } for (int i = 0; i < cj.Length; i++)//输出数组中每一个元素的值 { if (cj[i] < 60) //第几个成绩小于60 { Console.WriteLine("\n第{0}同学成绩为不及格,成绩为:{1}分\r\n", i + 1, cj[i]);//数组下标是从0开始的 } zf = zf + cj[i]; //总分 } Console.WriteLine("单科成绩总分为:{0}平均分为:{1}分", zf, zf / cj.Length); Console.ReadKey(); } } }
文章评论