C#学习之C#面向对象编程(类与实例、虚拟类、抽象类、封装类、静态类、范型) 您所在的位置:网站首页 虚拟猫猫 C#学习之C#面向对象编程(类与实例、虚拟类、抽象类、封装类、静态类、范型)

C#学习之C#面向对象编程(类与实例、虚拟类、抽象类、封装类、静态类、范型)

2024-07-11 19:52| 来源: 网络整理| 查看: 265

介绍

​ C#语言集成了Java和C++的优点,在全面的基础上发展了面向对象的概念,比如委托和范型,是从C++语言的函数指针和模版概念进化而来的,而单根的面向对象结构,即所有的对象都是继承于object类,这在很大程度上来自Java语言的思想。

主要内容:C#创建类和实例、虚拟类、抽象类、封装类、静态类、范型类、类的属性、方法、事件、C#语言委托远离和用法、匿名类、拓展方法

类和实例 1. 类和实例

​ C#引入类的概念,以全面支持对象编程。使用class关键字定义一个类,可以在类前面添加各种修饰符。

​ 类把实例抽象成一个概念,而实例具有类定义的这些特征。

​ 可以通过"[]“符号对类进行设置,一个比较重要的设置是”[Serializable]",这个设置表示当前的类是可序列化的。序列化的主要作用是类实例数据在传递的时候维持原样

[Serializable] public class Circle { }

​ C#语言使用new关键字调用类的构造方法创建类的实例。

Circle MyCircle = new Circle(); 2. 继承类

​ 所谓继承,就是一个类引入另一个类的内容,包括属性、方法是事件等。引入其他类的,称为子类,提供引入的类,称为父类。在子类中能够不必深;就调用父类的属性和方法。

​ 继承是面向对象机制中很重要的特征,经常作为标准用于判断编程语言是否位真正的面向对象语言。

​ C#是单继承的面向对象语言,而我们熟悉的Java也是单继承的语言,当C++是多继承的语言。

using System; namespace ExtendsDemo { public class Employee { public string Name { get; set; } public int Age { get; set; } } public class Manager : Employee { public Manager(string ManagerName, int ManagerAge) : base() { Name = ManagerName; Age = ManagerAge; } } /* 子类构造函数中的“:base()”,表示先调用父类的构造方法 */ } 3. 接口

​ 接口也是面向对象编程中的重要概念之一。许多精通面向对象的开发人员都建议针对接口编程,二不输出针对类编程。

​ 接口在更多的意义上是声明一种规范,继承接口的类都必须遵循这个规范。

​ C#编程语言的接口,具有以下特征:

接口不能被实例化,也不能包含字段接口的索引、属性、方法、事件等内容,只可声明而不可具体实现继承A接口的B接口,具有A接口的特征接口可以被多重继承,即接口或类可以同时继承多个接口 using System; namespace InterfaceDemo { public interface IEntry { string EmployeeRecords { get; set; } void Train(int EmployeeID); } } using System; namespace InterfaceDemo { public class NewEmployee { public int NewEmployeeID { get; set; } /// /// 保存新员工数据 /// /// 接口 public void SaveNewEmployee(IEntry employeeToSave) { // 获取接口中定义的属性 string RecordsData = employeeToSave.EmployeeRecords; // 调用接口中定义的方法 employeeToSave.Train(NewEmployeeID); } } } 4. 实现接口

​ 在C#语言中,继承接口的类必须实现接口定义的内容,否则就会出现编译错误。

using System; namespace InterfaceDemo { /// /// 新员工类,继承就职IEntry接口 /// class Employee : IEntry { public string TrainRecords { get; set; } // 实现员工档案的属性 string IEntry.EmployeeRecords { get; set; } // 实现员工档案的方法 void IEntry.Train(int EmployeeID) { TrainRecords = "公司制度培训和薪酬制度培训"; } /* 显式方式实现,在属性和方法名之前添加IEntry前缀,表名当前属性或者方法实现了IEntry接口 不能在属性或者方法前添加public前缀,因为接口声明的属性和方法必须允许外部程序访问,否则这种声明就没有意义 */ } /// /// 老员工类,继承就职IEntry接口 /// public class Record : IEntry { public string TrainRecords { get; set; } // 实现员工档案的属性 public string EmployeeRecords { get; set; } /// /// 实现员工档案培训的方法 /// /// public void Train(int EmployeeID) { TrainRecords = "薪酬制度培训"; } /* 隐式方法实现,不在属性或方法前添加"IEntry"前缀,必须将属性或方法的访问控制符定义为"public",非e编译程序不认为这个类实现了接口 */ } }

注意:接口的显式实现如果不声明访问权限修饰符,那么默认为public,如果添加访问权限修饰符,那么必须是public

5. 继承接口

​ 继承接口有两个含义,类继承接口,以及接口继承接口,类继承接口,必须要实现接口的声明。

​ 在一个系统中,客户的需求可能随时在改变,原有接口的修改意味着继承这个接口的所有类,都必须修改以实现接口,为了避免这种牵一发而动全身的灾难出现,我们可以借助于接口的继承来适应规范的改变。

using System; namespace InterfaceDemo { public interface IEntryExamine : IEntry { void Examine(int EmployeeID); } /// /// 新员工类,继承就职IEntry接口 /// class Employee : IEntryExamine { public string TrainRecords { get; set; } // 实现员工档案的属性 string IEntry.EmployeeRecords { get; set; } // 实现员工档案的方法 void IEntry.Train(int EmployeeID) { TrainRecords = "公司制度培训和薪酬制度培训"; } void IEntryExamine.Examine(int EmployeeID) { TrainRecords += "需要进行考试"; } } /// /// 老员工类,继承就职IEntry接口 /// public class Record : IEntry { public string TrainRecords { get; set; } // 实现员工档案的属性 public string EmployeeRecords { get; set; } /// /// 实现员工档案培训的方法 /// /// public void Train(int EmployeeID) { TrainRecords = "薪酬制度培训"; } } }

​ 可以使用接口的多继承,来完善员工入职的模型。

​ 接口的继承和多重继承,不仅为C#编程语言中类的继承增加了灵活性,更重要的是能以可扩展的方式,规范了类的设计和编码实现。

虚拟类、抽象类和封装类 1. 虚拟类

​ 虚拟类并不完全是虚拟的,致所有被称为“虚拟”,是因为C#编程语言借鉴了C++的“虚拟函数”的概念。在C++编程语言中,只有纯虚函数是没有实现代码的,所以“虚拟”的说法并不能确切表示虚拟类的特点,更多上是为了使用C++开发人员的习惯。

​ 实际上,完全可以在C#编程语言的虚拟方法中编写实现方法的代码。

​ 在C#编程语言中,虚拟类是一种可以在子类中覆盖其方法或者属性的类。

​ C#程序在类的内部,使用virtual声明一个方法或者属性,飙车在子类中可以覆盖这个方法或者属性。在子类的属性或方法声明之前,使用override关键字,表示当前方法或属性将覆盖父类定义的方法或属性。

​ 覆盖具有以下的原则:

不能覆盖非虚方法、非虚属性或静态方法、静态属性可以覆盖的方法或属性必须是virtual、abstract或override的。因为子类继承了父类的可覆盖特征,所以override的方法或属性还可以在子类中覆盖覆盖的方法或属性,不能更改原方法或属性的访问控制符、参数列表、返回数据类型和名称不能使用修饰符new、static、virtual或abstract修改覆盖的方法或属性

重写、重载、覆盖的概念

项目重写重载覆盖关键字无newoverride位置同一个类内部子类子类访问控制可以不一致可以不一致必须一致名称必须一致必须一致必须一致参数列表必须不一致可以不一致必须一致返回类型可以不一致可以不一致必须一致 using System; namespace DifferentClassDemo { public class MyWebPage { /// /// 显示类名称的方法,声明为virtual,表示可以在子类中覆盖该方法 /// /// /// protected virtual string ShowName(string Language) { switch (Language) { case "中文": return "我的页面"; case "English": return "MyWebPage"; default: return ""; } } /// /// 方法重载,参数类型不一致 /// /// protected string ShowName() { return "我的页面"; } } /// /// 错误界面,继承自MyWebPage /// public class ErrorPage : MyWebPage { /// /// 方法重写,访问控制不一致 /// /// public new string ShowName() { return "错误页面"; } } /// /// 登陆界面,继承自MyWebPage /// public class LoginPage : MyWebPage { /// /// 覆盖,与父类的方法一致 /// /// /// protected override string ShowName(string Language) { switch (Language) { case "中文": return "登陆页面"; case "English": return "LoginPage"; default: return ""; } } } }

​ 事实证明,虚拟类的虚拟方法具有很灵活的实现方式,这里的“虚拟”是从C++语言参考而来的概念,并不意味着虚拟方法不能在类中实现,只是在父类实现的方法,很有机会被子类覆盖得面目全非,失去了原来的功能而已。

2. 抽象类

​ 抽象的本意是抽取某些事物具有代表性的特征,作为一个概念来描述这些事物的共性。

​ 抽象的含义在这里表现为不能创建实例,抽象类只声明了需要实现的内容,具体的功能代码在继承它的子类中编写。

​ C#语言中的抽象类,仅仅提供了子类所必须包含的内容,而不能够创建实例。抽象类的作用和接口有些类似,当并不完全一样。在C#编程语言中,使用abstract关键字声明一个抽象类,抽象类有以下的特点:

抽象类不能使用new关键字创建类实例抽象类允许簇拥非抽象成员抽象类不能同时又是封装的继承抽象类的非抽象类,必须实现所有的抽象成员 using System; namespace DifferentClassDemo { /// /// 表示形状的类 /// public abstract class Shape { public int BasePointX { get; set; } public int BasePointY { get; set; } public abstract int MaxLength { get; set; } public abstract int MaxWidth { get; set; } public abstract decimal GetArea(); } public class Rectangle : Shape { public override int MaxLength { get; set; } public override int MaxWidth { get; set; } public override decimal GetArea() { return MaxLength * MaxWidth; } } } 3. 封装类

​ 抽象类不能声明为封装类,对于抽象类而言,抽象类可以被继承,实际上必须被继承,是不能创建实例的,封装类刚好相反,可以创建实例而不能被继承。

​ C#中不允许将一个类同时设置为抽象和封装,因为这样做是没有任何意义的。

​ C#编程语言使用关键字“sealed”表示一个封装类,封装类不能被继承。同样,“sealed”可以用于属性和方法,表示不能够在子类中覆盖。

using System; namespace DifferentClassDemo { /// /// 只能创建一个实例的类 /// public sealed class Singleton { static Singleton instance = null; private Singleton() { } public static Singleton Instance { get { if(instance == null) { instance = new Singleton(); } return instance; } } } }

​ 这个案例是典型的Singleton模式的实现方式之一,为了禁止外部程序创建重复的类实例,Singleton类需要屏蔽外部程序访问构造函数,同时只允许通过只读的Instance静态属性获取唯一的Singleton类实例。

​ 如果不把类声明成封装类,仍然可以通过继承的途径,重复创建实例。所以,必须堵上所有继承来创建Singleton类实例的途径,而使Singleton类不能被继承,就应该使用sealed关键字声明Singleton,也就是说,Singleton是一个封装类。

​ 因为封装在运行的时候不考虑继承、重载等因素,所以执行效率不比封装的类更高,但是封装类不能被继承,所以封装性能不足,在程序中编写封装类的时候,应当充分考虑系统设计的要求。

静态类和范型类

​ 静态类和范型类更多意义上是为了设计与编程上的方便而引入的,所谓静态类,就是不能创建实例的类,所有的属性和方法都通过类名称来调用。相对于静态类而言,范型类对于提高代码重用性的作用更为明显,范型的愿望其实很简单,就是在声明的时候不确定数据类型,在调用时才确定数据类型,范型的引入显著提高了代码的重用性。

​ 静态类和范型类注意作用是提高程序效率,并且使代码具有较高的重用性。虽然静态类看起来很死板,当Virtual Studio的LINQ和拓展方法等新技术都是借助静态方法来实现的,所以静态方法间接地提高了代码的重用性,仅仅把静态类当作全局变量来调用,还不能充分发挥它的作用。

1. 静态类

​ 在C#编程语言中,有一种用static关键字来声明的静态类,使用静态类或者Singleton模式可以满足全局变量和全局方法的要求。静态类并不能创建实例,所有的调用都是使用类名称进行,这相当于无论何时,静态类总能提供一个所有类都能调用的变量或者方法。

​ 在一个类中对静态的静态属性赋值,然后在另一个类中调用这个静态属性,因为没有创建类实例的过程,所有静态属性都能吧值是一个类原原本本地传递到另一个类中。静态类和设计模式中的Singleton模式有异曲同工之妙。

​ 静态类不是为了实现全局变量而设计的,静态类的真正作用,并不仅仅为了实现全局变量,C#语言的静态类有以下的特点:

静态类的所有成员都是静态的,无论是属性还是方法,都使用static关键字声明静态类的构造方法也是静态的,静态构造方法不能在子类中继承,并且不能带有private、public等控制访问符,也不能带有参数不能直接调用静态类的构造函数,在调用静态类成员时会自动执行而且仅执行一次 using System; namespace DifferentClassDemo { /// /// 将数字字符转换成十六进制字符串的静态类 /// public static class StrringToHexCode { /// /// 静态方法,将字符串转换成十六进制字符串 /// /// 需要转换的字符串 /// 已经转换了的字符串 public static string ConvertToHexCode(string value) { return Int32.Parse(value).ToString("x"); } /// /// 拓展静态方法,将字符串转换成十六进制字符串 /// /// 需要转换的字符串 /// 已经转换了的字符串 public static string ToHexCode(this string value) { return Int32.Parse(value).ToString("x"); } /* 拓展方法的使用: string HexCode = "5000".ToHexCode(); */ } }

​ 可以在静态类中写拓展方法,使用静态类的静态方法,使各种数据类型具有极强的拓展性。

​ 静态类这种特殊的类,大量用于基础架构的实现。

2. 范型类

​ 范型编程历来就是比较高级的编程理念,实现起来也是非常困难。

​ C++编程语言使用了模块的概念,使C++编程语言能够支持范型编程,C#编程语言的范型和C++编程语言的模版在实现的机制上稍有不同,但在使用上几乎没有区别。

​ .NET Framework从2.0就开始支持范型数据类型,这是范型对传统的面向对象编程有相当的冲击力。首先,一切对象基于object类的结构,被认为是“弱类型”的,相当于“强类型”的类,弱类型主要的劣势在于编译时不尽兴严格的数据类型检查,点a的执行速度也相对较慢。

​ 因为强类型编程提出了类型参数话的要求,简单的说,就是声明一个方法的参数时,一般情况下都是以“数据类型 参数名”的形式来声明的,因为强类型编程语言要严格检查参数的数据类型,所以这种代码在强类型的编程语言中重用性不高。

​ 这时,提供了另一种方法,生;参数的时候,暂时不指定参数的数据类型,用一个“T”符号先把位置占了,以后再把特定的数据类型替换上去。这个“T”符号已经被业界用了很久。

​ 范型编程带来的好处有以下三个方面:

降低强制转换时装箱操作带来的成本和风险为C#编程语言的强类型编程提供了支持提高C#程序代码的重用性 using System; using System.Collections; namespace DifferentClassDemo { /* 在范型类声明时使用where语句限定范型的数据类型,称为范型约束,代码中的“where T:class”语句,限定EmployeeList的元素只能是引用类型的数据类型 */ public class EmployeeList : CollectioinBase where T : class { public T this[int index] { get { return (T)List[index]; } set { List[index] = value; } } public int Add(T value) { return List.Add(value); } public void Remove(T value) { List.Remove(value); } } } using System; using System.Collections; namespace DifferentClassDemo { public class Employee { public string Name { get; set; } public int Age { get; set; } } public class Manager { public string Position { get; set; } public string Department { get; set; } } public class Test { static void Main(string[] args) { EmployeeList CurrentEmployees = new EmployeeList(); CurrentEmployees.Add(new Employee() { Name="Jack", Age = 54}); EmployeeList CurrentManagers = new EmployeeList(); CurrentManagers.Add(new Manager() { Department="技术部", Position="程序员"}); } } }


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有