变量与常量
1 变量的基本概念
变量本身被用来存储特定类型的数据,可以根据需要随时改动变量中所储存的数据值。变量具有名称、类型、值。变量名是变量在程序源代码中的标识。变量类型确定它所代表的的内存大小和类型,变量值是指它所代表的内存块中的数据。在程序的执行过程中,变量的值可以发生变化。使用变量之前必须声明变量,即指定变量的类型和名称。
2 变量的声明及赋值
变量的使用是程序设计中一个十分重要的环节。为什么要定义变量呢?就是要告诉编译器(Compiler)这个变量是属于哪一种数据类型,这样编译器才知道需要配置多少空间给它,以及它能存放什么样的数据。在程序运行的过程中,空间内的值是变化的,这个内存空间就称为变量。声明变量就是指定变量的名称和类型。声明变量可以一个类型后面跟多个变量,也可以在声明变量的同时给变量赋值。
string str1,str2;
int a=1,b=2,c=3;
3 变量的作用域
由于变量被定义出来后只是暂存在内存中,等到程序执行到某一个点后,该变量会被释放掉,也就是说变量有他的声明周期。变量的作用域是指程序代码能够访问该变量的区域,若超出该区域,则在编译时
会出现错误。
3.1 成员变量
在类体中定义的变量称为成员变量,成员变量在整个类中都有效。类的成员变量又可分为静态变量和实例变量俩种。
class Test
{
int x = 45; //实例变量
static int y = 90; //静态变量,也称类变量
}
如果在成员变量的前面加上关键字static,这样的成员变量称为静态变量。静态变量的有效范围可以跨类,甚至可以达到整个应用程序之内。对于静态变量,除了能在定义它的类内存取,还能直接以“类名.静态变量”的方式在其他类内使用。
3.2 局部变量
局部变量只在当前的代码块中有效。在类的方法中声明变量,包括方法的参数都属于局部变量。局部变量只有在当前方法中有效,不能用于类的其他方法。局部变量的生命周期取决于方法,当方法被调用时,C#编译器为方法中的局部变量分配内存空间,当该方法的调用结束后,则会释放方法中局部变量占用的内存空间,局部变量也会被销毁。
4 数据类型
C#中的变量类型根据其定义可以分为俩种:值类型和引用类型。这俩种类型的差异在于数据的储存方式,值类型的变量本身直接存储数据,而引用类型则存储实际数据的引用。
4.1 值类型
值类型变量直接存储其数据值,主要包含整数类型、浮点类型、布尔类型等。值类型变量在栈中进行分配,因此效率很高,使用值类型主要是为了提高性能。值类型具有如下特点:
- 值类型变量都存储在栈中。
- 访问值类型变量时,一般都是直接访问其实例。
- 每个值类型数据都有自己的数据副本,因此对一个值类型变量的操作不会影响其他变量。
- 复制值类型变量时,复制的是变量的值,而不是变量的地址。
- 值类型变量不能为null,必须具有一个确定的值。
值类型是从System.ValueType继承而来的类型。
4.1.1 整数类型
整数类型用来存储整数数值,即没有小数部分的值。可以使整数,也可以是负数。整形数据在C#中有三种表示形式:十进制、八进制、十六进制
十进制:不能以 0 作为数的开头。
八进制:必须以 0 作为数的开头。
十八进制:必须以 0X 或 0x 作为数的开头。
C#内置证书类型如下表:
类型 | 说明( 8 位等于 1 字节) | 范围 |
---|---|---|
sbyte | 8位有符号整数 | -28-1 ~ 28-1-1 |
short | 16位有符号整数 | -216-1 ~ 216-1-1 |
int | 32位有符号整数 | -232-1 ~ 2321-1 |
long | 64位有符号整数 | - 264-1 ~ 264-1-1 |
byte | 8位无符号整数 | 0 ~ 28-1 |
ushort | 16位无符号整数 | 0 ~ 216-1 |
unit | 32位无符号整数 | 0 ~ 232-1 |
ulong | 64位无符号整数 | 0 ~ 264-1 |
字节中存放的为二进制数字,所以范围是有计算公式的,就是通过二进制位数判断十进制的取值范围,有符号位数的第一个字节存放正负,其余存放二进制数字。
4.1.2 浮点类型
浮点类型变量主要用于处理含有小数的数值数据,浮点类型主要包含float和double俩种数值类型。
类型 | 说明 |
---|---|
float | 精确到7位数 |
double | 精确到15~16位数 |
如果不做任何设置,包含小数点的数值都别认为是double类型,如果要将数值以float类型来处理,需要通过强制使用 F 或 f 将其强制指定*float类型。如果要将float数值强制指定为double类型,可以使用d或D来记性转换,也可以不加。
如果需要时用float类型变量时,必须在数值的后边跟随f或F,否则编译器会直接将其作为double类型处理。
float a = 1.1f;
float b = 1.1F;
double c = 1.1d;
double d = 1.1D;
double e = 1.1;
4.1.3 浮点类型
布尔类型的值只能是 true 和 false。布尔类型变量不能与其他类型之间进行转化。
在定义全局变量时,整数类型和浮点类型的默认值为 0 ,布尔类型的默认值为 false。
4.2 引用类型
引用类型是构建C#应用程序的主要对象数据类型。在应用程序执行的过程中,预先定义的对象类型以new创建对象实例,并且储存在堆中。堆是一种系统以弹性配置的内存空间,没有特定大大小及存活时间,因此可以被弹性的运用于对象的访问。
引用类型就类似于生活中的代理商,代理商没有自己的产品,而是代理厂家的产品,使其就好像是自己的产品一样。
引用类型具有如下特征:
- 必须在托管堆中为引用类型变量分配内存。
- 使用new关键字来创建引用类型变量。
- 在托管堆中分配的每个对象都有与之相关联的附加成员,这些成员必须被初始化。
- 引用类型变量是由垃圾回收机制来管理的。
- 多个引用类型变量可以同时引用同一对象, 这种情况下对一个变量的操作会影响另一个变量所引用的同一对象。
- 引用类型被赋值前的值都是null。
所有被称为“类”的都是引用类型,主要包括类、接口、数组、委托
例:
using System;
namespace ConsoleApp1
{
class Program
{
class C
{
public int Value = 0;
}
static void Main(string[] args)
{
int v1 = 0;
int v2 = v1;
v2 = 927;
C c1 = new C();
C c2 = c1;
c2.Value = 112;
Console.WriteLine("Values:{0},{1}", v1, v2); // 0 ,927
Console.WriteLine("Refs:{0},{1}", c1.Value, c2.Value); // 112 ,112
Console.ReadLine();
}
}
}
5 值类型与引用类型的区别
从概念上看,值类型直接存储其值,而引用类型储存对其值得引用。这俩种类型储存在内存的不同地方。在C#中,必须在设计类型时就决定类型实例的行为。如果在编写代码时不能理解引用类型和值类型的区别,那么将会给代码带来不必要的异常。
从内存空间上看,值类型是在栈中操作,而引用类型则在堆中分配存储单元。栈在编译是就分配好空间,在代码中有栈的明确定义,而堆是程序运行中动态分配的内存空间,可以根据程序的运行情况动态的分配内存的大小。因此值类型总是在内存中占用一个预定义的字节数。而引用类型的变量则在堆中分配一个内存空间,这个内存空间包含的是对另一个内存位置的引用,这个位置是托管堆中的一个地址,即存放此变量实际值的地方。
也就是说值类型相当于现金,要用就直接用。而引用类型相当于存着,要用得先去银行取。
C#的所有值类型均隐式派生自System.ValueType,而System.ValueType直接派生于System.Object。即System.ValueType本身是一个类类型,而不是值类型。其关键在于ValueType重写了Equals()方法,而对值类型按照实际的值来比较,而不是引用地址来比较。
下面以一段代码来展示值类型与引用类型的区别:
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
ReferenceAndValue.Demonstration();
Console.ReadKey();
}
}
public class stamp
{
public string Name {
get; set; }
public int Age {
get; set; }
}
public static class ReferenceAndValue
{
public static void Demonstration()
{
stamp st1 = new stamp {
Name = "张三", Age = 25 };
stamp st2 = new stamp {
Name = "李四", Age = 18 };
int age = st1.Age;
st1.Age = 22;
stamp st3 = st2;
st2.Name = "王五";
Console.WriteLine("st1的年龄{0}",st1.Age); // 22
Console.WriteLine("年龄的值{0}",age); // 25
Console.WriteLine("st2的名字{0}",st2.Name); // 王五
Console.WriteLine("st3的名字{0}",st3.Name); // 王五
}
}
}
6 枚举类型
枚举类型是一种独特的值类型,,它用于声明一种具有相同性质的常量。编写与日期相关的应用程序时,经常需要用到年、月、日、星期等日期数据,可以将这些数据组织成多个不同名称的枚举类型。使用枚举可以增加程序的可读性、可维护性。同时枚举类型可以避免类型错误。
在定义枚举类型时,如果不对其进行赋值,默认情况下,第一个枚举类型的值位0,后面每个枚举数的值依次递增1。
在C#中使用关键则enum类声明枚举,其形式如下:
enum 枚举名
{
//枚举值名称 = 枚举值
list1 = value1,
list2 = value2,
list3 = value3,
...
listN = valueN
}
例:
using System;
namespace ConsoleApp1
{
class Program
{
enum MyDate
{
Sun = 0,
Mon = 1,
Tue = 2,
Wed = 3,
Thi = 4,
Fri = 5,
Sta = 6
}
static void Main(string[] args)
{
int k = (int)DateTime.Now.DayOfWeek;
switch (k)
{
case (int)MyDate.Sun:
Console.WriteLine("今天是星期日");
break;
case (int)MyDate.Mon:
Console.WriteLine("今天是星期一");
break;
case (int)MyDate.Tue:
Console.WriteLine("今天是星期二");
break;
case (int)MyDate.Wed:
Console.WriteLine("今天是星期三");
break;
case (int)MyDate.Thi:
Console.WriteLine("今天是星期四");
break;
case (int)MyDate.Fri:
Console.WriteLine("今天是星期五");
break;
case (int)MyDate.Sta:
Console.WriteLine("今天是星期六");
break;
default:
break;
}
Console.ReadKey();
}
}
}
7 类型转换
对于类型转换,就是用目标内存块去套取源内存中的数据,能套多少算多少。
7.1 隐式转换
所谓隐式转换就是不需要声明就能进行的转换。进行隐式转换,编译器不需要进行检查就能自动进行转换。下表列出了可以进行隐式转换的数据类型
源类型 | 目标类型 |
---|---|
sbyte | short、int、long、float、double、decimal |
byte | short、ushort、int、uint、long、ulong、float、double、decimal |
short | int、long、float、double、decimal |
ushort | int、uint、long、ulong、float、double、decimal |
int | long、float、double、decimal |
uint | long、ulong、float、double、decimal |
char | ushort、int、uint、long、ulong、float、double、decimal |
float | double |
ulong | float、double、decimal |
long | float、double、decimal |
当一种类型的值转换为大小相等或更大的另一类型时,发生扩大转换。当一种类型的值转换为比较小的另一种类型时,则发生收缩转化。
7.2 显式转换
显式转换也可以称为强制转换,需要在代码中明确地声明要转换的类型。如果要把高精度的变量值赋值给低精度的变量,就需要使用显式转换。
下表列出了需要进行显式转换的数据类型:
源类型 | 目标类型 |
---|---|
sbyte | byte、ushort、unit、ulong、char |
byte | sbyte 、char |
short | sbyte、byte、ushort、unit、ulong、char |
ushort | sbyte、byte、short、char |
int | sbyte、byte、short、ushort、unit、ulong、char |
uint | sbyte、byte、short、ushort、int、char |
char | sbyte、byte、short |
float | sbyte、byte、short、ushort、int、uint、long、ulong、char、decimal |
ulong | sbyte、byte、short、ushort、int、uint、long、char |
long | sbyte、byte、short、ushort、int、uint、ulong、char |
double | sbyte、byte、short、ushort、int、uint、ulong、long、char、decimal |
decimal | sbyte、byte、short、ushort、int、uint、ulong、long、char、decimal |
7.3 装箱和拆箱
将值类型转换为引用类型的过程叫做装箱,相反,将引用类型转换为值类型的过程叫做拆箱。在执行拆箱操作时,要符合类型一致的原则,否则会出现异常。
装箱是将一个值类型转换为一个对象类型(Object),而拆箱是将一个对象类型显式转换为一个值类型。对于装箱而言,它是将被装箱的值类型复制一个副本来转换。而对于拆箱而言,需要注意类型的兼容性,不能将一个值为string的object类型转换为int类型。
例:
using System;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
int a = 1;
Object ob1 = new Object();
ob1 = a; //装箱 将值类型转化引用类型
string st1 = "张三";
string st2 = "";
Object ob2 = st1;
st2 = (string)ob2; //拆箱 将引用类型转换为值类型
Console.WriteLine("ob1:{0}",ob1); // 1
Console.WriteLine("st2:{0}", st2); //张三
Console.ReadKey();
}
}
}
8 常量
常量就是值固定不变的量,而且常量的值在被编译时就已经确定。C#中用关键字const定义差常量,并且创建常量时必须设置初始值。常量就相当于每个公民的身份证号码,一点设置就不允许再修改。