标签:
面向对象编程
类是面向对象编程的3个主要特征---封装、继承和多态性---的基础。
封装允许隐藏细节。
继承
继承关系至少涉及两个类,其中一个类(基类)是另一个类的更泛化的版本。
为了从一个类型派生或者说从它继承,需要对那个基类型进行特化,这意味着要对基类型进行自定义,针对特定的目的调整它。
继承最关键的一点在于,所有派生类型都继承了基类型的成员。在派生类型中,
基成员的实现通常可以改动,但是不管是否修改基成员,派生类型中除了:有派生类型包含的那些
成员外,还可以包含基类型的成员。
多态性意味着一个方法或类型可以具有多种形式的实现。
对象和类
类相当于一个模块,对象是这个类的一个实例,相当于这个用这个模块创造一个产品。
使用类创建一个新对象的过程称为实例化。
实例化一个对象,需要使用new运算符指示“运行时”为一个对象分配内存,实例化对象,
并返回对实例的一个引用(整型数组)。
在堆中申请的内存(如new 申请分配),由垃圾回收器自动回收。(运行时,会在最后一次
访问对象之后,并在应用程序结束之前的某个时候,自动地回收内在。)
实例字段(成员变量)
可以在声明时设置字段的初始值。
实例方法(成员方法)
可以访问对象上的字段。
this关键字
在类的实例成员内部可以获取当前对象的一个引用。
this在概念上是传给每个实例方法的一个隐式参数,它返回对象本身的一个实例。
访问修饰符
在面向对象编程中,封闭不仅仅是将数据和行为组合到一起,它同时还意味着隐藏一个类中的数据,
使一个类的内部工作机制是最小程度地对类的外部公共,这减少了调用者对数据进行不恰当修改的几率。
访问修饰符是用途是提供封装。
标识了所修饰成员的封装级别。
public、private、protected、internal和protected internal 总共5个。
注:如果不为类成员添加访问修饰符,那么默认使用的是private。
属性
为了实现某些字段从外部只读等的效果。一般采取的办法是将字段标记为私有,然后提供取值
和赋值方法来访问和修改数据,只读时,只提供取值方法,不提供赋值方法。
C#提供了显式的语法支持:属性。
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 string firstName = "aaaaa", middleName = "bbbbbbbbb", lastName = "cccccccc";
6 string fullName;
7 Employee e = new Employee();
8 e.FirstName = firstName;
9 e.LastName = lastName;
10 Console.WriteLine(e.FirstName + "." + e.LastName);
11 }
12 }
13 public class Employee
14 {
15 private string _FirstName;
16 public string FirstName
17 {
18 set
19 {
20 _FirstName = value;
21 }
22 get
23 {
24 return _FirstName;
25 }
26 }
27 private string _LastName;
28 public string LastName
29 {
30 set
31 {
32 _LastName = value;
33 }
34 get
35 {
36 return _LastName;
37 }
38 }
39
40 }
属性的关键点在于,它提供了从编程角度看类似于字段的API,但是事实上,并不存在这样的字段。
在大括号中,要添加具体的属性实现代码。两个可选的部分构成了一个属性的实现。
set get
自动实现的属性 C#3.0
声明一个属性时,不实际地实现任何取值或同仁方法,也不声明任何支持字段
1 public class Employee
2 {
3
4 public string Title { set; get; }
5 public Employee Manager { set; get; }
6
7 }
使用属性的好处在于,如果需要添加一个额外代码:比如在set方法中添加验证,数据的合法性。
虽然属性的声明发生了变化,但是调用属性的代码不进行任何更改。
建议使用属性,来调用字段,不建议直接调用字段。
实现只读和只写属性:
通过移除某个属性的取值方法或赋值方法部分,可以改变属性的可访问性。
为取值方法和赋值方法指定访问修饰符
1 public class Employee
2 {
3 private string _FirstName;
4 public string FirstName
5 {
6 private set
7 {
8 _FirstName = value;
9 }
10 get
11 {
12 return _FirstName;
13 }
14 }
15 private string _LastName;
16 public string LastName
17 {
18 private set
19 {
20 _LastName = value;
21 }
22 get
23 {
24 return _LastName;
25 }
26 }
27 public string Title { private set; get; }
28 public Employee Manager { private set; get; }
29
30 }
注:这个访问修饰限制性必须比应用于整个属性的访问修饰符更为严格。
属性的赋值和取值,可以是任何操作,get 必须返回一个数据类型一致的数据。
属性和方法调用不允许作为ref或out参数值使用
原因:ref和out需要将内存地址传给目标方法,但是,由于属性可能是没有支持字段的虚字段,
也有可能是只读/只写的,因此不可能传递其基础存储的地址,不过可以通过中间变量来实现。
如:
1 public string Title
2 {
3 private set;
4 get
5 {
6 return _FirstName + "." + _LastName;
7 }
8 }
构造器(构造函数)
为了定义构造器,要创建一个返回类型的方法,而且方法名必须完全和类名相同。
构造器是用来创建对象实例的方法。
new运算符返回的是被实例佛手对象的类型。
注:如果一个字段在声明时赋了初始值,且在构造函数中也赋予了初始值,那么最终生效的是构造器
内部的赋值,它会覆盖声明时的任何赋值。
所以应该避免在两个地方同时赋值。
C#编译器会自动添加一个默认构造器(无参数),且如果自定义了构造函数,不再自动添加默认构造函数。
C#3.0对象初始化器
在调用的时候使用
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 string firstName = "aaaaa", middleName = "bbbbbbbbb", lastName = "cccccccc";
6 string fullName;
7 Employee e = new Employee(firstName, lastName) { Title = "MyTitle", Content="Content" };
8
9 Console.WriteLine(e.Title + ":" + e.Content + "" + ":" + e.FirstName + "." + e.LastName);
10 Console.ReadLine();
11
12
13 }
14
15
16 }
17 public class Employee
18 {
19 public Employee(string firstName, string lastName)
20 {
21
22 FirstName = firstName;
23 LastName = lastName;
24 }
25 private string _FirstName;
26 public string FirstName
27 {
28 private set
29 {
30 _FirstName = value;
31 }
32 get
33 {
34 return _FirstName;
35 }
36 }
37 private string _LastName;
38 public string LastName
39 {
40 private set
41 {
42 _LastName = value;
43 }
44 get
45 {
46 return _LastName;
47 }
48 }
49 public string Title
50 {
51 set;
52 get;
53
54 }
55 public string Content
56 {
57 set;
58 get;
59
60 }
61 public Employee Manager { set; get; }
62
63 }
终结器
终结器是在一个对象最后一次活动之后,并在程序终止之前执行,具体地说,垃圾回收器会在
一次垃圾回收过程中识别出带有终结器的对象。之后,它并不是立即回收这些对象,而是将它们添加
到一个终结列中。一个独立的线程遍历终结队列中的每一个对象,调用它们的终结器,然后把它们从队列
中删除,使它们再次可供垃圾回收器调用。
注:构造器可重载
使用this调用另一个构造器
这称为构造器链,它是用构造器初始化器来实现的。构造器初始化器会在执行当前构造器的实现之前
判断要调用另外哪一具构造器,并进行调用。
1 public Employee(string firstName, string lastName)
2 {
3
4 FirstName = firstName;
5 LastName = lastName;
6 }
7 public Employee(string Title, string firstName, string lastName)
8 : this(firstName, lastName)
9 {
10 this.Title = Title;
11 }
注:可写一个函数专门用来初始化,然后在构造器中调用。
匿名类型
在C#3.0中引入了对匿名类型的支持。它们是由器动态生成的数据,而不是通过显式的类定义来声明的。
var
编译器遇到匿名类型的语法时,就会自动生成一个CIL类,该类具有与匿名类型声明中的命名值
和数据类型对应的属性。
静态成员
使用static修饰
public static int Count = 0;
与实例字段不同,如果不对静态字段进行初始化,静态字段将自动获得相应数据类型的默认值。
另外,一个静态字段即使没有显式地赋值,也可以被访问。
在类的外部访问静态字段时,需要使用类名。
注:上下文就是作用域
静态方法
和静态字段一样,要直接在类名之后访问静态方法。
由于静态方法不是通过一个特定的实例来引用,所以this关键字在静态方法中是无效。
所以也不能直接在静态中访问类中声明的实例成员,所有实例成员必须通过一个对象引用才能访问。
静态构造器
除了静态字段和方法,C#还支持静态构造器。静态构造器用来对类进行初始化。静态构造器不是显式调用的
,运行时会在首次访问类时自动调用静态构造器。
所谓“首次访问类”,可能是调用一个普通构造器,也可能是访问类的一个静态方法或者字段。
在构造器中的赋值会覆盖声明时的初始化值。
静态属性
与实例属性一样,只是是属于类的。
使用静态属性几乎肯定要比使用公共静态字段好,因为公共静态字段在任何地方都能调用,而静态
属性则至少提供了一定程度的封装。
静态类
使用static修饰
这个类不包含任何实例成员,所以创建一个实例化的类是没有意义的。
静态类的另一个特殊在于,C#编译器会自动在CIL代码中把它标记为abstract和sealed。
这会将类指定为不可扩展,换言之,不能从它派生出其他类。
扩展方法(必须在静态类中定义)
C#3.0
引入了扩展方法(extension method)的概念。它能为一个不同的类模拟出一个实例方法。
使静态方法的第一个参数成为要扩展的类型,并在类型名称前面附加this关键字。
1 public static class DirectoryInfoExtension
2 {
3 public static string Message(this Employee e, string message1, string message2)
4 {
5 return message1 + message2;
6 }
7 }
8 string firstName = "aaaaa", middleName = "bbbbbbbbb", lastName = "cccccccc";
9 string fullName;
10 Employee e = new Employee();
11
12 Console.WriteLine(e.Title + ":" + e.Content + "" + ":" + e.FirstName + "." + e.LastName);
13
14 Console.WriteLine(e.Message(firstName,lastName));
15 Console.ReadLine();
C#通过这一处小小的改进,就使我们能为任何类添加实例方法,即使是那些不在同一个程序集中的类。然后,根据最终生成的CIL代码,你会发现扩展方法和一个普通的静态方法的代码是完全一样的。
扩展方法的要求如下:
1、第一个参数是要扩展或者操作的类型,这称为"被扩展的类型"
2、为了指定扩展方法,要在扩展的类型名称前面附加this修饰符
3、要将方法作为一个扩展方法来访问,要用using指令导入扩展类型的命名空间,或者
使扩展类和调用代码在同一个命名空间中。
注:如果扩展方法的签名已经和扩展类型中的一个签名匹配,扩展方法永远不会得到调用,除非是作为
一个普通的静态方法。
应该尽量少用扩展方法,而使用继承来扩展。
封装数据
两个特殊的字段修饰符,const、readonly
1、const
和const局部变量一样,const字段(常量字段)包含的是在编译时确定的一个值,它不可以在运行时改变。
常量字段自动成员静态字段,因为不需要为每个对象实例都生成一个新的字段实例。
但是,假如将一个常量字段显式地声明为static,会造成一个编译错误。
public 常量应该是恒定值,否则,如果对它进行了修改,那么在使用它的程序集中,不一定能反映出
这个修饰。需要重新编译。
将来可能改变的值应该指定为readonly,不要指定为常量。
2、readonly
和const不同,readonly修饰符只能用于字段(不能用于局部变量),它指出字段值只能从构造器
中更改,或者直接在声明时指定。
每个实例的readonly字段都可以是不同的,除此之外,readonly字段即可以是实例字段,也可以是静态
字段。另一个关键区别在于,可以在执行时为readonly字段赋值,而非只能在编译时赋值。
嵌套类
在类中除了定义方法和字段,还可以定义另一个类。这样的类称为嵌套类
嵌套类中的this成员指的是嵌套类的一个实例,而不是包容类。
嵌套类的另一个有趣的特点是它能够访问包容类的任何成员,其中包括私有成员。但反之则不然,包容类不能访问嵌套类的私有成员。
分部类
C#2.0新增的另一个语言特性是分部类(partial class)。
使用partial修饰
分部类是一个类的多个部分,这些部分可以合并成一个完整的类。
使用上下文关键字partial来声明一个分部类。
可以放在不同的文件当中。
分部类不允许对编译好的类(其他程序集中的类)进行扩展。只能利用分部类在同一个程序集
中将一个类的实现分解到多个文件中。
分部方法
C#3.0
使用partial修饰
分部方法只能存在于分部类中。另外和分部类相似,其主旨是为代码的生成提供方便。
分部方法允许声明一个方法而不需要一个实现。
然后,如果包含了可选的实现,这个实现就可以放到某个姊妹分部类定义中。
也就是声明和定义分别放在分部类的不同地方。
注:分部方法必须返回void,如果不是返回null,同时没有提供实现,那么调用一个未实现的方法。
返回什么才合理呢。所以只允许方法返回void,out参数在分部方法中是不允许的。如果
需要一个返回值,可以使用ref参数。
总之,分部方法使生成的代码能调用并非一要实现的方法。
五、C# 类
标签:
原文地址:http://www.cnblogs.com/tlxxm/p/4604522.html