封装是实现面向对象程序设计的第一步,封装就是将数据或函数等集合在一个个的单元中(咱们称之为类)。被封装的对象一般被称为抽象数据类型。html
面向对象程序设计中通常以类做为数据封装的基本单位。类将数据和操做数据的方法结合成一个单位。在设计类时,不但愿直接存取类中的数据,而是但愿经过方法来存取数据。如此就能够达到封装数据的目的,方便之后维护、升级,也能够在操做数据时多一层判断,提升安全性。sql
在C#中可以使用类来达到数据封装的效果,这样就可使数据与方法封装成单一元素,以便于经过方法存取数据。除此以外,还能够控制数据的存取方式。封装在C#中可以使用类来达到数据封装的效果,这样就可使数据与方法封装成单一元素,以便于经过方法存取数据。除此以外,还能够控制数据的存取方式。数据库
(一) 封装的意义
封装的意义在于保护或者防止代码(数据)被咱们无心中破坏。在面向对象程序设计中数据被看做是一个中心的元素而且和使用它的函数结合的很密切,从而保护它不被其它的函数意外的修改。编程
封装还能够解决数据存取权限问题,使用封装能够将数据隐藏起来,造成一个封闭的空间,用户能够设置哪些数据只能在这个空间中使用,哪些数据能够在空间外部使用。若是一个类中包含敏感数据,则有些用户能够访问,有些用户却不能访问。若是不对这些数据的访问加以限制,那么后果是很严重的。因此,在编写程序时,要对类的成员使用不一样的访问修饰符,从而定义它们的访问级别。 封装提供了一个有效的途径来保护数据不被意外的破坏。相比咱们将数据(用域来实现)在程序中定义为公用的(public)咱们将它们(fields)定义为私有的(privat)在不少方面会更好。私有的数据能够用两种方式来间接的控制。第一种方法,咱们使用传统的存、取方法。第二种方法咱们用属性(property)。c#
使用属性不只能够控制存取数据的合法性,同时也提供了“读写”、“只读”、“只写”灵活的操做方法。数组
(二)访问修饰符安全
一、Public 访问修饰符ide
Public 访问修饰符容许一个类将其成员变量和成员函数暴露给其余的函数和对象。任何公有成员能够被外部的类访问。函数
下面的实例说明了这点:post
using System; namespace RectangleApplication { class Rectangle { //成员变量 public double length; public double width; public double GetArea() { return length * width; } public void Display() { Console.WriteLine("长度: {0}", length); Console.WriteLine("宽度: {0}", width); Console.WriteLine("面积: {0}", GetArea()); } }// Rectangle 结束 class ExecuteRectangle { static void Main(string[] args) { Rectangle r = new Rectangle(); r.length = 4.5; r.width = 3.5; r.Display(); Console.ReadLine(); } } }
当上面的代码被编译和执行时,它会产生下列结果:
长度: 4.5 宽度: 3.5 面积: 15.75
在上面的实例中,成员变量 length 和 width 被声明为 public,因此它们能够被函数 Main() 使用 Rectangle 类的实例 r 访问。
成员函数 Display() 和 GetArea() 能够直接访问这些变量。
成员函数 Display() 也被声明为 public,因此它也能被 Main() 使用 Rectangle 类的实例 r 访问。
二、Private 访问修饰符
Private 访问修饰符容许一个类将其成员变量和成员函数对其余的函数和对象进行隐藏。只有同一个类中的函数能够访问它的私有成员。即便是类的实例也不能访问它的私有成员。
下面的实例说明了这点:
using System; namespace RectangleApplication { class Rectangle { //成员变量 private double length; private double width; public void Acceptdetails() { Console.WriteLine("请输入长度:"); length = Convert.ToDouble(Console.ReadLine()); Console.WriteLine("请输入宽度:"); width = Convert.ToDouble(Console.ReadLine()); } public double GetArea() { return length * width; } public void Display() { Console.WriteLine("长度: {0}", length); Console.WriteLine("宽度: {0}", width); Console.WriteLine("面积: {0}", GetArea()); } }//end class Rectangle class ExecuteRectangle { static void Main(string[] args) { Rectangle r = new Rectangle(); r.Acceptdetails(); r.Display(); Console.ReadLine(); } } }
当上面的代码被编译和执行时,它会产生下列结果:
请输入长度: 4.4 请输入宽度: 3.3 长度: 4.4 宽度: 3.3 面积: 14.52
在上面的实例中,成员变量 length 和 width 被声明为 private,因此它们不能被函数 Main() 访问。成员函数 AcceptDetails() 和 Display() 能够访问这些变量。因为成员函数 AcceptDetails() 和 Display() 被声明为 public,因此它们能够被 Main() 使用 Rectangle 类的实例 r 访问。
三、Protected 访问修饰符
Protected 访问修饰符容许子类访问它的基类的成员变量和成员函数。这样有助于实现继承。咱们将在继承的章节详细讨论这个。更详细地讨论这个。
四、Internal 访问修饰符
Internal 访问说明符容许一个类将其成员变量和成员函数暴露给当前程序中的其余函数和对象。换句话说,带有 internal 访问修饰符的任何成员能够被定义在该成员所定义的应用程序内的任何类或方法访问。
下面的实例说明了这点:
using System; namespace RectangleApplication { class Rectangle { //成员变量 internal double length; internal double width; double GetArea() { return length * width; } public void Display() { Console.WriteLine("长度: {0}", length); Console.WriteLine("宽度: {0}", width); Console.WriteLine("面积: {0}", GetArea()); } }//end class Rectangle class ExecuteRectangle { static void Main(string[] args) { Rectangle r = new Rectangle(); r.length = 4.5; r.width = 3.5; r.Display(); Console.ReadLine(); } } }
当上面的代码被编译和执行时,它会产生下列结果:
长度: 4.5 宽度: 3.5 面积: 15.75
在上面的实例中,请注意成员函数 GetArea() 声明的时候不带有任何访问修饰符。若是没有指定访问修饰符,则使用类成员的默认访问修饰符,即为 private。
五、Protected Internal 访问修饰符
Protected Internal 访问修饰符容许在本类,派生类或者包含该类的程序集中访问。这也被用于实现继承。
(三) 现实中的封装:
咱们的房子就是一个类的实例,室内的装饰与摆设只能被室内的居住者欣赏与使用,若是没有四面墙的遮挡,室内的全部活动在外人面前一览无遗。因为有了封装,房屋内的全部摆设均可以随意地改变而不用影响他人。然而,若是没有门窗,一个包裹得严严实实的黑箱子,即便它的空间再宽阔,也没有实用价值。房屋的门窗,就是封装对象暴露在外的属性
和方法,专门供人进出,以及流通空气、带来阳光
(四) 封装的例子:
举一个咱们项目开发中常常用到的例子,对数据库操做,封装成一个经常使用的帮助类,咱们进行重复调用,下面为其中封装的一个方法
private DataTable Getdate(string sqlstr, SqlParameter parameter) { SqlConnection conn = new SqlConnection(str);(这个str是链接数据库) SqlCommand comm = new SqlCommand(sqlstr,conn); if (parameter!=null) { comm.Parameters.AddWithValue(parameter.ParameterName, parameter.Value); } SqlDataAdapter adapter = new SqlDataAdapter(comm); DataTable dt = new DataTable(); adapter.Fill(dt); return dt; }
继承是OOP最重要的特性之一。任何类均可以从另一个类继承,即这个类拥有它所继承类的全部成员。在OOP中,被继承的类称为父类或基类。
C#提供了类的继承机制,但C#只支持单继承,不支持多重继承,即在C#中一次只容许继承一个类,不能同时继承多个类。利用继承机制,用户能够经过增长、修改或替换类中方法对这个类进行扩充,以适应不一样的应用要求。利用继承,程序开发人员能够在已有类的基础上构造新类。继承使得类支持分类的概念。在平常生活中不少东西比较有条理,那是由于它们有着很好的层次分类。若是不用层次分类,则要对每一个对象定义其全部的性质。使用继承后,每一个对象就能够只定义本身的特殊性质。每一层的对象只需定义自己的性质,其余性质能够从上一层继承下来。
在C#中,接口容许多继承,能够经过继承多个接口来实现相似于C++中的多重继承。在继承一个基类时,成员的可访问性是一个重要的问题。子类不能访问基类的私有成员,可是能够访问其公共成员。子类和外部代码均可以访问公共成员。这就是说,只使用这两个可访问性,就可让一个成员被基类和子类访问,同时也能够被外部的代码访问。
为了解决这个问题,C# 还提供了第3种可访问性:protected。只有派生类才能访问protected成员,基类和外部代码都不能访问protected成员。
除了成员的保护级别外,用户还能够为成员定义其继承行为。基类的成员能够是虚拟的,成员能够由继承它的类重写。子类能够提供成员的其余执行代码。这种执行代码不会删除原来的代码,仍能够在类中访问原来的代码,但外部代码不能访问它们。若是没有提供其余执行方式,外部代码就访问基类中成员的执行代码。
虚拟成员不能是私有成员,由于成员不能同时由子类重写,也不能访问它。基类还能够定义为抽象类。抽象类不能直接实例化,要使用抽象类就必须继承这个类,而后再实例化。
继承主要实现重用代码,节省开发时间。
(一)、C#中的继承符合下列规则
1.继承是可传递的。若是C从B中派生,B又从A中派生,那么C不只继承了B中声明的成员,一样也继承了A中的成员。Object类做为全部类的基类。
2.派生类应当是对基类的扩展。派生类能够添加新的成员,但不能除去已经继承的成员的定义。
3.构造函数和析构函数不能被继承。除此以外的其它成员,不论对它们定义了怎样的访问方式,都能被继承。基类中成员的访问方式只能决定派生类可否访问它们。
4.派生类若是定义了与继承而来的成员同名的新成员,就能够覆盖已继承的成员。但这并不由于这派生类删除了这些成员,只是不能再访问这些成员。
5.类能够定义虚文法、虚属性以及虚索引指示器,它的派生类可以重载这些成员,从而实现类能够展现出多态性。
(二)、new关键字
若是父类中声明了一个没有friend修饰的protected或public方法,子类中也声明了同名的方法。则用new能够隐藏父类中的方法。(不建议使用)
(三)、base关键字
base关键字用于从派生类中访问基类的成员:
1.调用基类上已被其余方法重写的方法。
2.指定建立派生类实例时应调用的基类构造函数。
(四)基类和派生类
一个类能够派生自多个类或接口,这意味着它能够从多个基类或接口继承数据和函数。
C# 中建立派生类的语法以下:
<acess-specifier> class <base_class> { ... } class <derived_class> : <base_class> { ... }
假设,有一个基类 Shape,它的派生类是 Rectangle:
using System; namespace InheritanceApplication { class Shape { public void setWidth(int w) { width = w; } public void setHeight(int h) { height = h; } protected int width; protected int height; } // 派生类 class Rectangle: Shape { public int getArea() { return (width * height); } } class RectangleTester { static void Main(string[] args) { Rectangle Rect = new Rectangle(); Rect.setWidth(5); Rect.setHeight(7); // 打印对象的面积 Console.WriteLine("总面积: {0}", Rect.getArea()); Console.ReadKey(); } } }
当上面的代码被编译和执行时,它会产生下列结果:
总面积: 35
(五)基类的初始化
派生类继承了基类的成员变量和成员方法。所以父类对象应在子类对象建立以前被建立。您能够在成员初始化列表中进行父类的初始化。
下面的程序演示了这点:
using System; namespace RectangleApplication { class Rectangle { // 成员变量 protected double length; protected double width; public Rectangle(double l, double w) { length = l; width = w; } public double GetArea() { return length * width; } public void Display() { Console.WriteLine("长度: {0}", length); Console.WriteLine("宽度: {0}", width); Console.WriteLine("面积: {0}", GetArea()); } }//end class Rectangle class Tabletop : Rectangle { private double cost; public Tabletop(double l, double w) : base(l, w) { } public double GetCost() { double cost; cost = GetArea() * 70; return cost; } public void Display() { base.Display(); Console.WriteLine("成本: {0}", GetCost()); } } class ExecuteRectangle { static void Main(string[] args) { Tabletop t = new Tabletop(4.5, 7.5); t.Display(); Console.ReadLine(); } } }
当上面的代码被编译和执行时,它会产生下列结果:
长度: 4.5
宽度: 7.5
面积: 33.75
成本: 2362.5
(六)C# 多重继承
多重继承指的是一个类别能够同时从多于一个父类继承行为与特征的功能。与单一继承相对,单一继承指一个类别只能够继承自一个父类。
C# 不支持多重继承。可是,您可使用接口来实现多重继承。下面的程序演示了这点:
using System; namespace InheritanceApplication { class Shape { public void setWidth(int w) { width = w; } public void setHeight(int h) { height = h; } protected int width; protected int height; } // 基类 PaintCost public interface PaintCost { int getCost(int area); } // 派生类 class Rectangle : Shape, PaintCost { public int getArea() { return (width * height); } public int getCost(int area) { return area * 70; } } class RectangleTester { static void Main(string[] args) { Rectangle Rect = new Rectangle(); int area; Rect.setWidth(5); Rect.setHeight(7); area = Rect.getArea(); // 打印对象的面积 Console.WriteLine("总面积: {0}", Rect.getArea()); Console.WriteLine("油漆总成本: ${0}" , Rect.getCost(area)); Console.ReadKey(); } } }
当上面的代码被编译和执行时,它会产生下列结果:
总面积: 35
油漆总成本: $2450
(一)、多态定义
派生类的实例由基类的实例加上派生类新增的成员组成。派生类的引用指向整个类对象,包括基类部分。若是有一个派生类对象的引用,就能够获取对象基类部分的引用(强制类型转换)。
同一种操做做用于不一样的类的对象,不一样的类的对象进行不一样的执行,最后产生不一样的执行结果。简单理解:让一种对象表现出来多种类型。
以下简单的例子:
使用这种方式,派生类能够访问基类和派生类中的全部成员,可是基类只能访问基类中的成员。这是咱们想要的结果吗?
(二) 多态的实现方式(虚方法):
一、定义:
在基类中用virtual关键字声明的方法叫作虚方法,在派生类中能够经过override关键字进行重写
二、使用场景:
1. 明肯定义了基类,对基类中的方法进行重写时,能够考虑使用虚方法。
2.派生类的方法和基类的方法有相同的签名和返回类型。
3.覆写和被覆写的方法具备相同等级的可访问性。
三、例子:
class Person { private string _name; public string Name { get { return _name; } set { _name = value; } } public Person(string name) { this.Name = name; } public virtual void SayHello() { Console.WriteLine("我是人类"); } } class Chinese:Person { public Chinese(string name) : base(name) { } public override void SayHello() { Console.WriteLine("你好,个人姓名是{0},我是中国人",this.Name); } } class Japanese:Person { public Japanese(string name) : base(name) { } public override void SayHello() { Console.WriteLine("你好,个人姓名是{0},我是日本人", this.Name); } } class Program { static void Main(string[] args) { Chinese c1 = new Chinese("小明"); Chinese c2 = new Chinese("小李"); Japanese j1 = new Japanese("井泉宜良"); Japanese j2 = new Japanese("龟田君"); Person[] Pers = { c1, c2, j1, j2 }; //Pers[0] = c1; //Pers[1] = c2; //Pers[2] = j1; //Pers[3] = j2; 上面数组的另一种写法 for (int i = 0; i < Pers.Length; i++) { Pers[i].SayHello(); } //Person p = new Chinese("小明");//Japanese("bingbian"); //p.SayHello(); Console.ReadKey(); } }
四、加深拓展
(三) 多态的实现方式(抽象方法)
一、定义:
描述共性的类就是抽象类,抽象类中的方法用abstract关键字修饰。其中抽象类并不考虑其实现的具体方式。
二、成员:
抽象方法和非抽象方法;属性;字段;构造函数;实例成员
三、使用场景:
1在抽象类定义以前,若是其余类中,没有公共的基类,那么能够经过抽象出来共同的基类来实现共性的方法
2 若是父类中的方法有默认的实现,而且父类须要实例化,这时候能够考虑将 父类定义成一个普通类,用虚方法来实现多态
四、例子:
class Program { static void Main(string[] args) { Animal an = new Dog(); an.Speak(); Console.ReadKey(); } } public abstract class Animal { public abstract void Speak(); } class Dog:Animal { public override void Speak() { Console.WriteLine("狗汪汪的叫"); } } class Cat:Animal { public override void Speak() { Console.WriteLine("猫喵喵的叫"); } }
五、注意事项:
1. 抽象类成员必须标记为abstract,而且不能有任何实现,抽象成员必须下抽象类中
2.抽象类不能被实例化
3.子类继承抽象类后,必须把父类中的全部抽象成员都重写(除非子类也是一个抽象类,则不须要重写)
4.抽象成员的访问修饰符不能是private
5.在抽象类中能够包含实例成员,而且抽象类的实例成员能够不被子类实现
6.抽象类中是有构造函数的,虽然不能被实例化
7.若是父类的抽象方法中参数,那么,继承这个抽象父类的子类在重写父类的方法时候必须传入对应的参数。若是抽象父类的抽象方法中有返回值,那么子类在重写这个抽象方法的时候也必需要传入返回值
8.若是父类中的方法有默认的实现,而且父类须要实例化,这时候能够考虑将 父类定义成一个普通类,用虚方法来实现多态
9.若是父类中的方法没有默认的实现。父类也不能实例化,则能够将该类定义为抽象类
(四) 多态的实现方式(接口)
一、接口的定义:
接口是一种编程规范、能力,c#自己并不支持类的多重继承,可是经过接口能够实现类的多重继承
二、成员:
方法;索引器;事件;自动属性(没有字段和方法体的属性);不能有构造函数和字段
三、使用场景:
要继承多个基类时,基类能够抽象为接口
四、例子:
class Program { static void Main(string[] args) { Ichlssable ch = new Teacher(); ch.CHLSS(); Console.ReadKey(); } } interface Ichlssable { void CHLSS(); } class Driver { public void KaiChe() { Console.WriteLine("我是一名司机,能够开车"); } } class Teacher:Driver,Ichlssable { public void Teach() { Console.WriteLine("我是一名老师,能够教书"); base.KaiChe(); } public void CHLSS() { Console.WriteLine("我是人类,能够吃喝拉撒睡"); } }
五、注意事项:
1.一个类继承了接口,这个类就须要实现接口中的全部成员
2.接口不能被实例化,也就是说不能用new建立对象
3.接口的成员中不能加访问修饰符,默认的为public,且不能被修改,接口中的成员不能有任何实现
4.接口和接口之间能够继承,而且能够多继承
5.接口不能够继承一个类,而类能够继承一个接口(接口只能继承类,类能够继承类和接口)
6.实现接口的子类必须实现该接口的所有成员
7.一个类能够同时继承一个类和多个接口,那么被继承类的名必须写在接口名称的前面,并用,隔开
8.显示实现接口的目的是为了解决方法的重命名问题,当继承的接口中的方法和参数如出一辙时u,就要用的显示的实现接口。,当一个抽象实现接口的时候,须要子类去实现接口。
9.接口中不能包含实现的方法