c#的值类型可以分为以下几种:
●简单类型(Simple types)
●结构类型(Struct types)
●枚举类型(Enumeration types)
简单类型
表4-1 整数类型
数据类型
特征 取值范围 sbyte
有符号 8位整数 在-128到127之间
byte无符号8位整数 在0到255之间
short有符号16位整数 在-32768到32767之间
ushort无符号16位整数 在0到65535之间
int有符号32位整数 在-2147483648到2147483647之间
uint无符号32位整数 0到4294967295之间
long有符号64位整数 在-9223372036854775808到9223372036854775807之间
ulong无符号64位整数 0和18446744073709551615之间
布尔类型
在C#中,分别采用true和false两个值来表示。
注意:在C和C++中,用0来表示“假”,其它任何非0的式子都表示“真”。这种不正规的表达在C#中已经被废弃了。在C#中,true值不能被其它任何非零值所代替。在其它整数类型和布尔类型之间不再存在任何转换,将整数类型转换成布尔类型是不合法的:
bool x=1 //错误,不存在这种写法。只能写成x=true或x=false
实数类型
●单精度:取值范围在正负 1.5×10-45 到 3.4×1038 之间,精度为 7 位数。
●双精度:取值范围在正负 5.0×10-324 到 1.7×10308 之间,精度为 15 到 16 位数。
C#还专门为我们定义了一种十进制类型(decimal),主要用于方便我们在金融和货币方面的计算。
十进制类型是一种高精度、128 位数据类型,它所表示的范围从大约 到 的 28 至 29 位有效数字。注意,该精度是用位数(digits)而不是以小数位(decimal places)来表示的。运算结果准确到 28 个小数位。十进制类型的取值范围比double类型的范围要小得多,但它更精确。
当定义一个decimal变量并赋值给它时,使用m下标以表明它是一个十进制类型,如:decimal d_value=10.m;
如果省略了m,在变量被赋值之前,它将被编译器当作双精度(double)类型来处理。
字符类型
C#提供的字符类型按照国际上公认的标准,采用Unicode字符集。一个Unicode的标准字符长度为 16 位,用它可以来表示世界上大多数语言。
另外,我们还可以直接通过十进制转义符(前缀\x)或Unicode表示法给字符型变量赋值(前缀\u),如下面对字符型变量的赋值写法都是正确的:char c='\x0061'; //c 为小写字母 a ,0x61(16进制) == 97(10进制)
char c='\u0061'; //c 为小写字母 a 。也是 16 进制形式。
注意:在C和C++中,字符型变量的值是该变量所代表的ASCⅡ码,字符型变量的值作为整数的一部分,可以对字符型变量使用整数进行赋值和运算。而这在C#中是被禁止的。
表4-2 转义符(Escape Sequences)
转义符 字符名
\0空字符
\a感叹号(Alert)
\n新行
\r回车
结构类型
struct PhoneBook{
public string name;
public string uint age;
public string phone;
public struct address{
public string city;
public string street;
public uint no;
}
}
枚举类型
为枚举类型的元素所赋的值的类型限于long、int、short和byte等整数类型。
enum WeekDay
{
Sunday, //值为 0, 也可以写成 Sunday = 11, 则后面的依次加 1 ,分别为 12, 13, ...
Monday, //值为 1
Tuesday, //值为 2
Wensday //值为 3
...
}
C#中的引用类型有四种:
●类
●代表
●数组
●接口
类
类是一种包含数据成员、函数成员和嵌套类型的数据结构。
- 类的数据成员有常量、域和事件。
- 函数成员包括方法、属性、索引指示器、运算符、构造函数和析构函数。 类和结构同样都包含了自己的成员,但它们之间最主要的区别在于:类是引用类型,而结构是值类型。
在C#语言中取消了指针这个概念。当然,对指针恋恋不舍的程序员仍然可以在C#中使用指针,但必须声明这段程序是“非安全(unsafe)”的。而我们在这里要介绍的是C#的一个引用类型--代表(delegate)。
代表(delegate)
代表在C#是类型安全的。
在代表的实例中,我们可以封装一个静态方法,也可以封装一个非静态的方法。我们看下面的例子:
程序清单4-2:
using System;
delegate int MyDelegate();//声明一个代表
public class MyClass
{
public int InstanceMethod(){
console.WriteLine("Call the instance method.");
return 0;
}
static public int StaticMethod(){
Console.WriteLine("Call the static method.");
return 0;
}
}
public class Test
{
static public void Main()
{
MyClass p=new MyClass();
//将代表指向非静态的方法 InstanceMethod
MyDelegate d=new MyDelegate(p.InstanceMethod);
//调用非静态方法
d();
//将代表指向静态的方法StaticMethod
d=new MyDelegate(MyClass.StaticMethod);
//调用静态方法
d();
}
}
程序的输出结果是:call the instance method.
call the static method.
例二:
using System;
using System.Collections.Generic;
using System.Text;namespace DelegateDemo
{
class Class1
{
delegate double processDelegate(double db1, double db2);
static double Multiply(double db1, double db2)
{
return db1 * db2;
}
static double Divide(double db1, double db2)
{
return db1 / db2;
}
static void NamedMethod(string strInput,double dbNum1,double dbNum2)
{
processDelegate process;
if (strInput == "M")
process = new processDelegate(Multiply);
else
process = new processDelegate(Divide);
Console.WriteLine("结果为:{0}", process(dbNum1, dbNum2));
}
static void AnonymousMethod(string strInput, double dbNum1, double dbNum2)
{
processDelegate process;
if (strInput == "M")
process = delegate(double db1, double db2){return db1 * db2;};
else
process = delegate(double db1, double db2) { return db1 / db2; };
Console.WriteLine("结果为:{0}", process(dbNum1, dbNum2));
}
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main(string[] args)
{
//
// TODO: 在此处添加代码以启动应用程序
Console.WriteLine("请输入两个小数,用逗号分割");
string strInput = Console.ReadLine();
int commmaPos = strInput.IndexOf(',');
double dbNum1 = Convert.ToDouble(strInput.Substring(0, commmaPos));
double dbNum2 = Convert.ToDouble(strInput.Substring(commmaPos + 1));
Console.WriteLine("输入M表示乘法,或者D表示除法");
strInput = (Console.ReadLine()).ToUpper();
//使用命名方法
Console.WriteLine("使用命名方法委托");
NamedMethod(strInput,dbNum1,dbNum2);
//使用匿名方法
Console.WriteLine("使用匿名方法委托");
AnonymousMethod(strInput, dbNum1, dbNum2);
//Console.Read();
}
} }
数组
在C#中数组可以是一维的也可以是多维的,同样也支持矩阵和参差不齐的数组。
一维数组最为普遍,用的也最多。我们先看一看下面的例子:
程序清单4-3:
using System:
class Test
{
static void Main(){
int[] arr=new int[5];
for(int i=0;i〈arr.Length;i++)
arr[i]=i*i;
for(int i=0;i〈arr.Length;i++)
Console.WriteLine("arr[{0}]={1}",i,arr[i]);
}
}这个程序创建了一个基类型为int型的一维数组,初始化后逐项输出。其中arr.Length表示数组元素的个数。程序的输出为:arr[0]=0
arr[1]=1
arr[2]=4
arr[3]=9
arr[4]=16上面的例子中我们用的是一维的,很简单吧!下面我们介绍多维的:
class Text
{
static void Main(){ //可动态生成数组的长度
string[] a1; //一维string数组
string[,] a2 //二维
string[,,] a3 //三维
string[][] j2; //可变数组(数组)
string[][][][] j3; //多维可变数组
}在数组声明的时候可以对数组元素进行赋值,或者叫做对数组的初始化。也可以在使用的时候进行动态赋值。看下面的例子:class Test
{
static void Main(){
int[] a1=new int[] {1,2,3};
int[,] a2=new int[,] {{1,2,3},{4,5,6}};
int[,,] a3=new int[10,20,30];
int[][] j2=new int[3][];
j2[0]=new int[] {1,2,3};
j2[1]=new int[] {1,2,3,4,5,6};
j2[2]=new int[] {1,2,3,4,5,6,7,8,9};
}
}
上面的例子我们可以看出数组初始化可以用几种不同的类型,因为它要求在一个初始化时要确定其类型。比如下面的写法是错误的:class Test
{
static void F(int[] arr){}
static void Main(){
F({1,2,3}); //错误
}
}
因为数组初始化时{1,2,3}并不是一个有效的表达式。我们必须要明确数组类型:class Test
{
static void F(int[] arr) {}
static void Main(){
F(new int[] {1,2,3});
}
}
例:
声明:static int Find(int value,int[] array){...}
可以这样调用:Console.WriteLine(Find(3,new int[] {5,4,3,2,1}));
C#的装箱(boxing)和拆箱(unboxing)
装箱和拆箱机制使得在C#类型系统中,任何值类型、引用类型和object(对象)类型之间进行转换,我们称这种转化为绑定连接。
简单地说,有了装箱和拆箱的概念,对任何类型的值来说最终我们都可以看作是object类型。
装箱转换是指将一个值类型隐式地转换成一个object类型,或者把这个值类型转换成一个被该值类型应用的接口类型(interface-type)。
隐士装箱
int i=10;
object obj=i;
显式装箱
int i=10;
object obj=object(i);
我们可以假想存在一个boxing类型,其声明如下:
class T_Box
{
T value;
T_Box(Tt){
value=t;
}//该类型的构造函数
}
这里T表示将要装箱的值的类型,它可以是int、char、enum等等。现在我们要将类型为T的值v装箱,其执行过程为:执行new T_Box(v),将返回结果的实例作为对象类型的值,那么下面的语句:int i=10;
object obj=i;等价于:
int i=10;
object obj=new int_Box(i); //装i装箱成对象obj
程序清单4-4:
using System
class Test{
public static void Main(){
int i=10;
object obj=i; //对象类型
if(obj is int){
Console.Write("The value of i is boxing!");
}
i=20; //改变i的值
Console.WriteLine("int:i={0}",i);
Console.WriteLine("object:obj={0}",obj);
}
}输出结果为:
The value of i is boxing!
int:i=20;
object:obj=10;这就证明了,被装箱的类型的值是作为一个拷贝赋给对象的。
拆箱的过程分为两步:
首先,检查这个对象实例,看它是否为给定的值类型的装箱值。
然后,把这个实例的值拷贝给值类型的变量。 我们举个例子来看看一个对象拆箱的过程。
int i=10;
object obj=i;
int j=(int)obj;
必须注意,装箱转换和拆箱转换必须遵循类型兼容原则。
C#中的变量和常量
为变量起名时要遵守C#语言的规定:
●变量名必须以字母开头
●变量名只能由字母、数字和下划线组成,而不能包含空格、标点符号、运算符等其它符号。
●变量名不能与C#中的关键字名称相同。这些关键字我们在附录A中给出。
●变量名不能与C#中的库函数名称相同。
但在C#中有一点是例外,那就是允许在变量名前加前缀“@”。在这种情况下,我们就可以使用前缀“@”加上关键字作为变量的名称。这主要是为了与其他语言进行交互时避免冲突。因为前缀“@”实际上并不是名称的一部分,其它的编程语言就会把它作为一个普通的变量名。在其它情况下,我们不推荐使用前缀“@”作为变量名的一部分。
下面给出了一些合法和非法的变量名的例子:
int i; //合法
int No.1; //不合法,含有非法字符
string total; //合法
char use; //不合法,与关键字名称相同
char @use; //合法
float Main; //不合法,与函数名称相同
在C#语言中,我们把变量分为七种类型,它们分别是:静态变量(static varibles),
非静态变量(instance variables),
数组元素(array elements),
值参数(value parameters),
引用参数(reference parameters),
输出参数(output parameters),
局部变量(local variables)。
看下面的例子:
class A
{
public static int x;
int y;
void F(int[] v,int a,ref int b,out int c){
int i=1;
c=a+b++;
}
}在上面的变量声明中,x是静态变量,y是非静态变量,v[0]是数组元素,a是值参数,b是引用参数,c是输出参数,i是局部变量。
常量修饰符constant-modifier可以是:
●new
●public
●protected
●internal
●private
常量的类型type必须是以下之一:
sbyte,byte,short,ushort,int,uint,long,ulong,char,float,double,decimal,bool,string,枚举类型(enum-type),或引用类型(reference).