一个变量只不过是一个供程序操作的存储区的名字。在 C# 中,每个变量都有一个特定的类型,类型决定了变量的内存大小和布局。范围内的值可以存储在内存中,可以对变量进行一系列操作。
在这里,data_type 必须是一个有效的 C# 数据类型,可以是 char、int、float、double 或其他用户自定义的数据类型。variable_list 可以由一个或多个用逗号分隔的标识符名称组成。
变量通过在等号后跟一个常量表达式进行初始化(赋值)。初始化的一般形式为:
variable_name = value;
变量可以在声明时被初始化(指定一个初始值)。初始化由一个等号后跟一个常量表达式组成,如下所示:
一些实例:
int d = 3, f = 5; /* 初始化 d 和 f. /
byte z = 22; / 初始化 z. /
double pi = 3.14159; / 声明 pi 的近似值 /
char x = 'x'; / 变量 x 的值为 'x' */
正确地初始化变量是一个良好的编程习惯,否则有时程序会产生意想不到的结果。
请看下面的实例,使用了各种类型的变量:
class Program
{
static void Main(string[] args)
{
short a;
int b ;
double c;
/* 实际初始化 */
a = 10;
b = 20;
c = a + b;
Console.WriteLine("a = {0}, b = {1}, c = {2}", a, b, c);
Console.ReadLine();
}
}
当上面的代码被编译和执行时,它会产生下列结果:
a = 10, b = 20, c = 30
类型推断使用var关键字。声明变量的语法有些变化:使用var关键字替代实际的类型。编译器可以根据
变量的初始化值“推断”变量的类型。例如
var someNumber=0;
就变成:
int someNumber=0;
即便someNumber从来没有声明为int,编译器也可以确定,只要someNumber在其作用域内,就是int类
需要遵循以下一些规则:
变量的作用域是可以访问该变量的代码区域,一般情况下,确定作用域遵循以下规则:
大型程序在不同部分为不同的变量使用相同的变量名很常见。只要变量的作用域是程序的不同
部分,就不会有问题,也不会产生多义性。但要注意,同名的局部变量不能在同一作用域内声明两
次。例如,不能使用下面的代码:
class Program
{
static int Main(string[] args)
{
int j = 20;
for (int i = 0; i < 10; i++)
{
int j = 30;
Console.WriteLine(j + i);
}
return 0;
}
}
如果试图编译它,就会产生如下错误:
error CS0136:A local variable namedj’cannot be declared in
this scope because that name is used in an enclosing local scope
to define a local or parameter
其原因是:变量j是在for循环开始之前定义的,在执行for循环时仍处于其作用域内,直到MainO
方法结束执行后,变量j才超出作用域。第2个j(不合法)虽然在循环的作用域内,但作用域嵌套
在Main0方法的作用域内。因为编译器无法区分这两个变量,所以不允许声明第二个变量
某些情况下,可以区分名称相同(尽管其完全限定名不同)、作用域相同的两个标识符。此时编译器允许声明第二个变量。原因是C#在变量之间有一个基本的区分,它把在类型级别声明的变量看成字段,而把在方法中声明的变量看成局部变量
class Program
{
static int j = 20;
static int Main(string[] args)
{
int j = 30;
Console.WriteLine(j);
return 0;
}
}
虽然在Main0方法的作用域内声明了两个变量j,这段代码也会编译:一个是在类级别上定义的j,在类Program删除前(在本例中,是Main0方法终止,程序结束时)是不会超出作用域的:一个是在Main0中定义的j。这里,在Main0方法中声明的新变量j隐藏了同名的类级别变量,所以在运行这段代码时,会显示数字30。
顾名思义,常量是其值在使用过程(生命周期)中不会发生变化的变量。在声明和初始化变量时,在变量的前面加上关键字const,就可以把该变量指定为一个常量:
constinta=100://This value cannot be changed
在 C# 中,变量分为以下几种类型:
值类型变量可以直接分配给一个值。它们是从类 System.ValueType 中派生的。
值类型直接包含数据。比如 int、char、float,它们分别存储数字、字母、浮点数。当您声明一个 int 类型时,系统分配内存来存储值。
下表列出了 C#中可用的值类型:

如需得到一个类型或一个变量在特定平台上的准确尺寸,可以使用 sizeof 方法。表达式 sizeof(type) 产生以字节为单位存储对象或类型的存储尺寸。下面举例获取任何机器上 int 类型的存储尺寸:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Size of int: {0}", sizeof(int));
Console.ReadLine();
}
}
当上面的代码被编译和执行时,它会产生下列结果:
Size of int: 4
引用类型不包含存储在变量中的实际数据,但它们包含对变量的引用。
换句话说,它们指的是一个内存位置。使用多个变量时,引用类型可以指向一个内存位置。如果内存位置的数据是由一个变量改变的,其他变量会自动反映这种值的变化。内置的 引用类型有:object、dynamic 和 string。
当一个值类型转换为引用类型时,则被称为 装箱;另一方面,当一个引用类型转换为值类型时,则被称为 拆箱。
装箱:值类型转化为引用类型的过程。在堆上为新生成的引用对象分配内存,然后将值类型的数据拷贝到分配的内存中,返回堆中新分配对象的地址,这个地址就是指向对象的引用(如下图)

o 和 i 的改变将互不影响,因为装箱使用的是 i 的一个副本。
对值类型在堆中分配一个对象实例,并将该值复制到新的对象中。按三步进行。
可以看出,进行一次装箱要进行分配内存和拷贝数据这两项比较影响性能的操作。
拆箱:引用类型转化为值类型的过程。获取引用类型的地址,将用用对象的值拷贝到栈上的值类型实例中(注意 拆箱时可能会引发“转换无效”的异常。要记住,拆箱时强转的值类型,应以装箱时的值类型一致)(如下图)

经过这2步,可以认为是同boxing是互反操作。严格意义上的拆箱,并不影响性能,但伴随这之后的拷贝数据的操作就会同boxing操作中一样影响性能。
PS:
注意:只有装过箱的数据才能拆箱。
对象(Object)类型 是 C# 通用类型系统(Common Type System - CTS)中所有数据类型的终极基类。Object 是 System.Object 类的别名。所以对象(Object)类型可以被分配任何其他类型(值类型、引用类型、预定义类型或用户自定义类型)的值。但是,在分配值之前,需要先进行类型转换。
您可以存储任何类型的值在动态数据类型变量中。这些变量的类型检查是在运行时发生的。
声明动态类型的语法:
dynamic <variable_name> = value;
例如:
dynamic d = 20;
动态类型与对象类型相似,但是对象类型变量的类型检查是在编译时发生的,而动态类型变量的类型检查是在运行时发生的。
字符串(String)类型 允许您给变量分配任何字符串值。字符串(String)类型是 System.String 类的别名。它是从对象(Object)类型派生的。字符串(String)类型的值可以通过两种形式进行分配:引号和 @引号。
例如:
String str = "qq.com";
一个 @引号字符串:
@“qq.com”;
C# string 字符串的前面可以加 @(称作"逐字字符串")将转义字符(\)当作普通字符对待,比如:
string str = @"C:\Windows";
等价于:
string str = "C:\\Windows";
@ 字符串中可以任意换行,换行符及缩进空格都计算在字符串长度之内。
string str = @"";
用户自定义引用类型有:class、interface 或 delegate。
指针类型变量存储另一种类型的内存地址。C# 中的指针与 C 或 C++ 中的指针有相同的功能。声明指针类型的语法:
type* identifier;
例如:
char* cptr;