1. 值类型
C#的值类型可以分为以下几种:
简单类型Simple types
结构类型Struct types
枚举类型Enumeration types
1.1.1 整数类型
数据类型 | 说明 | 取值范围 | 类结构 |
sbyte | 有符号8位整数 | -128~127 | SByte |
byte | 无符号8位整数 | 0~255 | Byte |
short | 有符号16位整数 | -32768~32767 | Int16 |
ushort | 无符号16位整数 | 0~65535 | UInt16 |
int | 有符号32位整数 | -2147483648-2147483647 | Int32 |
uint | 无符号32位整数 | 0~4294967295 | UInt32 |
long | 有符号64位整数 | -9223372036854775808~9223372036854775807 | Int64 |
ulong | 无符号64位整数 | 0~18446744073709551615 | UInt64 |
1.1.2 实数类型
单精度 取值范围在正负 1.5×10(−45方)到3.4×10(38方)之间精度为7 位数
双精度 取值范围在正负 5.0×10(−324方)到1.7 ×10(308方)之间精度为15 到16 位数
1.2.1 委托
委托类型 (delegate type) 表示对具有特定参数列表和返回类型的方法的引用。通过委托,我们能够将方法作为实体赋值给变量和作为参数传递。委托类似于在其他某些语言中的函数指针的概念,但是与函数指针不同,委托是面向对象的,并且是类型安全的。
如果我们声明了自己的一个委托,那么它就是对系统定义的类System.delegate 的一个扩展.在委托的实例中我们可以封装一个静态方法也可以封装一个非静态的方法
我们看下面的例子
程序清单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.
1.2.2 数组
数组的初始化的例子
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};
}
}
C#还专门为我们定义了一种十进制类型(decimal) 主要用于方便我们在金融和货
币方面的计算.十进制类型的取值范围比double 类型的范围要小得多但它更精确.使用 m 下标以表明它是一个十进制类型.如:
decimal d_value = 1.0m;
1.1.3 字符类型
C#提供的字符类型按照国际上公认的标准采用Unicode 字符集.一个Unicode 的标准字符长度为16 位.
char c = 'A';
1.2 引用类型
C#中的引用类型有四种
类
委托(delegate)
数组
接口
我们还可以直接通过十六进制转义符前缀\x 或Unicode 表示法给字符型
变量赋值.前缀\u 如下面对字符型变量的赋值写法都是正确的
char c = '\x0032';
char c = '\u0032';
1.1.4 结构类型
把一系列相关的变量组织成为一个单一实体的过程,我们称为生成结构的过程.这个单一实体的类型就叫做结构类型,每一个变量称为结构的成员结构类型的变量,采用struct 来进行声明.比如我们可以定义通讯录记录结构
struct PhoneBook {
public string name;
public uint age;
public string phone;
public struct address{
public string city;
public string street;
public uint no;
}
}
1.1.5 枚举类型
枚举enum 实际上是为一组在逻辑上密不可分的整数值提供便于记忆的符号.
比如:我们声明一个代表星期的枚举类型的变量
enum WeekDay
Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
};
WeekDay day;
注意:结构是由不同类型的数据组成的一组新的数据类型,结构类型的变量的值是由各个成员的值组合而成的.而枚举则不同,枚举类型的变量在某一时刻只能取枚举中某一个元素的值.比如:day 这个表示星期的枚举类型的变量,它的值要么是Sunday ,要么是Monday ,或其它的星期元素.但它在一个时刻只能代表具体的某一天,不能既是星期二,又是星期三
day = Tuseday;
按照系统的默认枚举中的每个元素类型都是int 型,且第一个元素删去的值为0
.它后面的每一个连续的元素的值按加1 递增.在枚举中,也可以给元素直接赋值.如
下把星期天的值设为1, 其后的元素的值分别为2,3…
enum WeekDay
Sunday=1, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
};
为枚举类型的元素所赋的值的类型限于long int short 和byte 等整数类型
2.1成员的可访问性
一、C#中已声明的可访问性:
1. public:访问不受限制;
2. internal:访问仅限于所属程序集;
3. protected:访问仅限于当前类和其子类;
4. internal protected:访问仅限于当前程序集或其子类(子类可以不属于当前程序集);
5. private:访问仅限于包含该成员的类型。
二、默认可访问性
1. 命名空间(namespace)隐式地具有public访问性,且不允许有访问修饰符;
2. 编辑单元或命名空间中声明的类型可以具有public或internal可访问性,默认为internal;
3. 类(class)的默认访问级别为public;
4. 结构体(struct)默认访问级别为private;
5. 接口(interface)默认访问基本为internal;但是如果在其他public类中使用时,必须为public;
6. 类和结构体成员(成员函数和成员变量)默认访问级别为private;(但是声明为命名空间成员的类型只能具有public或internal可访问性);
7. 接口的成员隐式地具有public可访问性,在接口成员的声明中不允许使用访问修饰符;
6. 枚举的成员隐式地具有public可访问性,声明中不允许使用访问修饰符。
三、可访问性约束
1. 类类型的直接基类必须至少与该类类型本身具有相同的可访问性;
2. 接口类型的基接口必须至少与该接口类型本身具有相同的可访问性;
3. 委托类型的返回类型和参数类型必须至少与该委托类型本身具有相同的可访问性;
4. 常量的类型必须至少与该常量本身具有相同的可访问性;
5. 域的类型必须至少与该域本身具有相同的可访问性;
6. 方法的返回类型和参数类型必须至少与该方法本身具有相同的可访问性;
7. 属性的类型必须至少与该属性本身具有相同的可访问性;
8. 事件的类型必须至少与该事件本身具有相同的可访问性;
9. 索引器的类型和参数类型必须与该索引器本身具有相同的可访问性;
10. 运算符的返回类型和参数类型必须至少与该运算符本身具有相同的可访问性;
11. 实例构造函数的参数类型必须至少与该实例构造函数本身具有相同的可访问性。
四、可访问域
1. 预定义类型(例如object、int或double)可访问域是无限的;
2. 在程序P中声明的顶级类型(不是在某个类型中声明的成员)T的可访问域是指:
1).如果T的已声明可访问性为public,则T的可访问域将是P以及引用P的任何程序的程序文本;
2).如果T的已声明可访问性为internal,则T的可访问域将是P的程序文本。
可见,定及类型的可访问域始终至少是在其中声明该类型的程序的程序文本。
3. 在程序P的类型T中声明的嵌套成员(在其他类型中声明的成员)M,其可访问域是如下所指之一(M本身也可能是一个类型):
1).如果M的已声明可访问性为public,则M的可访问域将是T的可访问域;
2).如果M的已声明可访问性为internal protected,设D表示P的程序文本和所有从T派生的类型(这些类型是在P的外部声明的)的程序文本的并集,则M的可访问域将是T与D的可访问域的交集;
3).如果M的已声明可访问性为protected,设D表示T的程序文本和所有从T派生的类型的程序文本的并集,则M的可访问域将是T与D的可访问域的交集;
4).如果M的已声明可访问性为internal,则M的可访问域将是T的可访问域与P的程序文本的交集;
5).如果M的已声明可访问性为private,则M的可访问域将是T的程序文本。
可见,嵌套成员的可访问域总是至少为声明该成员的类型的程序文本,并且绝不会大于声明该成员的类型的可访问域。
例
public class A
{
public static int X;
internal static int Y;
private static int Z;
}
internal class B
{
public static int X;
internal static int Y;
private static int Z;
public class C
{
public static int X;
internal static int Y;
private static int Z;
}
private class D
{
public static int X;
internal static int Y;
private static int Z;
}
}
在这个例子中,类和成员具有以下可访问域:
1) A和A.X的可访问域无限制;
2) A.Y、B、B.X、B.Y、B.C、B.C.X和B.C.Y的可访问域是包含这段代码的程序的程序文本;
3) A.Z的可访问域是A的程序文本;
4) B.Z和B.D的可访问域是B的程序文本,包括B.C和B.D的程序文本;
5) B.C.Z的可访问域是B.C的程序文本;
6) B.D.X和B.D.Y的可访问域是B的程序文本,包括B.C和B.D的程序文本;
7) B.D.Z的可访问域是B.D的程序文本。
例:基类的所有成员(实例构造函数、析构函数和静态构造函数除外)都由派生类型继承,这些成员甚至包括基类的私有成员。然而,私有成员的可访问域只包括声明该成员的类型的程序文本。在下面的示例中,类B继承了类A的私有成员x:
class A
{
int x; //类的成员的默认可访问性为private
static void F(B b){
b.x = 1; //允许
}
}
class B: A
{
static void F(B b){
b.x = 1; //错误,不能访问x
}
}
由于该成员是私有的,所以只能在A的类主体中对他进行访问。因此,A.F方法中对b.x的访问可以成功,但是B.F方法中对b.x的访问却会造成错误。
2.2.2 访问修饰符
类修饰符:public、internal、partial、abstract、sealed、static
成员修符:public、protected、private、internal、sealed、abstract、virtual、override、readonly、const
Public:最开放,所有的本程序集以及其他的程序集里面的对象都能够访问
Protected:比较开放,自身成员以及子类成员可访问
Private:只有自身成员才能够访问
Internal:本程序集内的成员可以访问
Partial:部分类,可以将一个类分成几部分写在不同文件中,最终编译时将合并成一个文件,且各个部
分不能分散在不同程序集中
Abstract:修饰类的时候表示该类为抽象类,不能够创建该类的实例。修饰方法的时候表示该方法需要
由子类来实现,如果子类没有实现该方法那么子类同样是抽象类;且含有抽象方法的类一定是抽象类
Sealed:修饰类时表示该类不能够被继承,修饰方法时表示该方法不能被覆写。
Static:修饰类时表示该类时静态类,不能够实例化该类的对象,既然不能实例化该类,那么这个类也
就不能够含有对象成员,即该类所有成员为静态;修饰类成员时,该成员为类成员,只能通过【类.成员名】的方
式访问
当static修饰构造函数时,构造函数不能含有任何参数,不能含有修饰符,构造函数不能对对象成员进
行初始化操作。但是能够对静态成员进行初始化或者调用。不能保证他在什么时候执行,却能保证在第一次使用类
型前执行。在静态构造函数中初始化的静态成员为最终初始化结果。例如:
public static int test = 0;
static Person()
{
test = 3;
}
static void Main(string[] args)
{
Console.WriteLine(Person.test);//运行结果为3
}
也就是说相当于:
public static int test;
static Person()
{
test=3;
}
Virtual:修饰方法成员,表示虚方法。父类可以含有该类的实现,子类可以覆写该函数。
Override:表示该方法为覆写了父类的方法。
Readonly:修饰字段,表示该字段为只读字段。
注意:readonly修饰引用类型时由于操作不当可能修改该只读对象状态。例如:
Readonly List<Person> persons=….;
我们可能在某些地方对persons 进行了修改:persons.add(new Person());
Const:修饰字段,表示该字段为只读字段。并且在编译时必须能够明确知道该字段的值,其值是硬编码
到程序中去的,修改了该类型成员后需要重新编译才能使修改生效。
而readonly是运行时只读,内容在运行时确定,所以修改了readonly类型成员后无需重新编译即可生效
。
Readonly不能修饰局部变量,const可以。
注意:当一个类或方法没有被任何修饰符修饰时,默认为internal:
C#用多种修饰符来表达类的不同性质。根据其保护级C#的类有五种不同的限制修饰符:
public可以被任意存取;
protected只可以被本类和其继承子类存取;
internal只可以被本组合体(Assembly)内所有的类存取,组合体是C#语言中类被组合后的逻辑单位和物理单位,
其编译后的文件扩展名往往是“.DLL”或“.EXE”。
protected internal唯一的一种组合限制修饰符,它只可以被本组合体内所有的类和这些类的继承子类所存取。
private只可以被本类所存取。
如果不是嵌套的类,命名空间或编译单元内的类只有public和internal两种修饰。
new修饰符只能用于嵌套的类,表示对继承父类同名类型的隐藏。
abstract用来修饰抽象类,表示该类只能作为父类被用于继承,而不能进行对象实例化。抽象类可以包含抽象的成
员,但这并非必须。abstract不能和new同时用。下面是抽象类用法的伪码:
abstract class A
{
public abstract void F();
}
abstract class B: A
{
public void G() {}
}
class C: B
{
public override void F()
{
//方法F的实现
}
}
抽象类A内含一个抽象方法F(),它不能被实例化。类B继承自类A,其内包含了一个实例方法G(),但并没有实现抽象
方法F(),所以仍然必须声明为抽象类。类C继承自类B,实现类抽象方法F(),于是可以进行对象实例化。
sealed用来修饰类为密封类,阻止该类被继承。