五、C# 类

面向对象编程

类是面向对象编程的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参数。

总之,分部方法使生成的代码能调用并非一要实现的方法。

时间: 2024-10-18 06:18:48

五、C# 类的相关文章

深入struts2.0(五)--Dispatcher类

1.1.1       serviceAction方法 在上个Filter方法中我们会看到如下代码: this.execute.executeAction(request, response, mapping); 图3.2.1  Filter方法中跳转到action图 而在ExecuteOperations类中(excute是一个实例)有如下代码: public void executeAction(HttpServletRequest request, HttpServletResponse

Java解惑五:类之谜

本文是依据JAVA解惑这本书,做的笔记.电子书见:http://download.csdn.net/detail/u010378705/7527721 谜题46 函数重载的问题. JAVA重载解析过程:1. 选取全部可用的方法或者构造器:2. 从过程1中选取的方法或构造器中选择最精确的. 一般而言:能够强制要求编译器选择一个精确的重载版本号,将实參转型为形參所声明的类型. 谜题47 继承中静态域的问题. 静态域由声明它的类及其全部子类共享. 假设须要让每个子类都具有某个域的单独拷贝,必须在每个子

C++语言笔记系列之十五——派生类、基类、子对象的构造和析构函数调用关系

例子 example 1 注:若一个基类同时派生出两个派生类,即两个派生类从同一个基类继承,那么系统将为每一个简历副本,每个派生类独立地使用自己的基类副本(比如基类中有属于自己类的静态变量等). #include <iostream.h> class Person { public: person() {cout<<"Construction of person."<<endl;} ~person() {cout<<"Destr

谈谈Delphi中的类和对象:五、类是一种对数据和操作高度的封装机制;六、类是一种代码重用机制

五.类是一种对数据和操作高度的封装机制 1)数据封装 unit Unit2; interface type TEmployee = class; private FName: String; public Constructor Create; function GetName: String; procedure SetName(AName: String); end; implementation constructor TEmployee.Create; begin FName:= 'Xu

JavaScript学习总结(十五)——Function类

在JavaScript中,函数其实是对象,每个函数都是Function类的实例,既然函数对象,那么就具有自己的属性和方法,因此,函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定. 一.函数的声明 方式一:常规方式 1 function sum1(num1,num2){ 2 return num1+num2 3 } 方式二:函数表达式 1 var sum2=function(num1,num2){ 2 return num1+num2; 3 }; 方式三:动态创建函数(这种方式用得不多)

JavaSE学习笔记(五)——类与对象

一.  类和对象 1.1           面向对象与面向过程的区别 1.面向过程 采用面向过程必须了解整个过程,每个步骤都有因果关系,每个因果关系都构成了一个步骤,多个步骤就构成了一个系统,因为存在因果关系每个步骤很难分离,非常紧密,耦合度高,当任何一步骤出现问题,将会影响到所有的系统.如:采用面向过程生产电脑,那么他不会分CPU.主板和硬盘,它会按照电脑的工作流程一次成型. 2.面向对象 面向对象对会将现实世界分割成不同的单元(对象),实现各个对象,如果完成某个功能,只需要将各个对象协作起

快学Scala-第五章 类

知识点: 1.简单类和无参方法 class Counter { private var value = 0 //必须初始化字段 def increment() { value += 1} //方法默认是公有的 def current() = value } 在Scala中,类并不声明为public,Scala源文件可以包含多个类,所有这些类都具有共有可见性. val myCounter = new Counter //or new Counter() myCounter.increment()

第五节 类和对象

面向对象怎么理解? 1.1 面向对象是相对面向过程而言 1.2 面向对象和面向过程都是一种思想 1.3 面向过程:强调的是功能行为 1.4 面向对象:将功能封装进对象,强调具备了功能的对象 1.5 面向对象是基于面向过程的 特点: 三个特征:封装.继承.多态 我们在开发过程中,就是在找对象进行使用,没有对象就new一个对象. 找对象:建立对象,使用对象,维护对象的关系 封装: (1)是指隐藏对象的属性和实现细节,仅对外提供公共访问方式. (2)好处:将变化隔离:便于使用:提供重用性:提高安全性

PHP学习笔记二十五【类的继承】

<?php //定义父类 class Stu{ public $name; protected $age; protected $grade; private $address;//私有变量不会被继承 //当一个子类的方法和父类的方法完全一样,称为方法的重写 public function __construct(){ echo "Stu 构造函数"; } public function showInfo() { echo $this->name."||"

Java多线程系列五——列表类

参考资料: http://xxgblog.com/2016/04/02/traverse-list-thread-safe/ 一些列表类及其特性  类 线程安全 Iterator 特性 说明 Vector 是 fail-fast 内部方法用synchronized修饰,因此执行效率较低 1. 线程安全的列表类并不意味着调用它的代码就一定线程安全 2. 只有CopyOnWriteArrayList能支持在遍历时修改列表元素 ArrayList 否 fail-fast 在多线程环境中使用不当易出错