C#|10、接口、抽象、密封、开放封闭原则

0开放封闭原则 定义:软件实体应该可以扩展,但是不可以修改。
特性:对扩展是开放的,对修改是封闭的。开放即可以操作,关闭即不可以操作。
1、接口声明

  • API:Application Programming Interace的缩写,就是应用程序接口
  • 接口:指描述可属于任何类或结构的一组相关功能。就是一种规范、功能。
  • 组成:可由属性、方法、事件、索引器或者四种成员类型的任意组合构成。(类中可以有字段、属性、方法,接口不能有字段)
  • 接口的声明格式:
    修饰符 interface 接口名称
    {
    属性
    方法
    索引器
    事件
    }
    注意:接口名称习惯以I开头,跟在I后的第一个字符也是大写的,结尾一般以able结尾,如IFlyable
C#|10、接口、抽象、密封、开放封闭原则
文章图片

C#|10、接口、抽象、密封、开放封闭原则
文章图片

接口声明注意事项
  • 接口不能包含字段
  • 接口成员不允许添加访问修饰符,默认就是public, 成员也不能夹absract访问修饰符
  • 接口不能包含实现其成员的任何代码,而只能定义成员本身(例如不允许写具有方法体的函数),可以有返回值,返回值不受影响
  • 实现过程必须在实现接口的类中完成。
  • 接口的成员可以使方法、属性、事件和索引器,单不能包含常数、字段、运算符、实例构造函数、析构函数或类型,也不能包括任何种类的静态成员
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 接口声明 { interface IEatable { //接口默认声明名为Public(类的默认声明是private) //接口中不能有访问修饰符 //由于接口不能有字段,所以属性经常被写作自动属性 //声明属性 string Name { get; set; } //声明方法,方法中不能包含方法体 void Write(); //无返回值 string Read(); //有返回值 } }

2、接口的实现和继承
  • 实现过程必须在实现接口的类中完成。
  • 类继承具有单根型,接口可多重继承
  • 父接口也称为改接口的显示基接口
  • 接口多重继承时,派生接口名与父接口用冒号隔开,多个父接口之间用逗号隔开
  • 接口的成员不能同名。继承的成员不用再声明,单接口可以定义与继承而来的成员同名成员,这称之为接口成员覆盖了继承而来的成员,这不会导致错误,但编译器会给出一个警告关闭警告提示的方法是成员在声明前加上一个new关键字。但如果没有覆盖父接口中的成员,使用new关键字会导致编译器发出警告。
2.1实例讲解接口的作用 老鹰(eagle)、麻雀(sparrow)鸵鸟(ostrich)都是鸟类,根据三者的共性,提取出鸟类作为父类;并且各自具有各自的特点,老鹰吃小鸡,麻雀吃粮食,鸵鸟吃青草
老鹰和麻雀都会非,如何实现这个飞功能
实现方法:
  • 在父类中添加“飞”,但是鸵鸟不会飞,所以不能加在父类中;
  • 在会费的子类中添加“飞”功能,完全可以,但是违背开放封闭原则,下次我新加一个天鹅(swan),我们还要去远吗去查看“飞功能如何实现的”,然后再天鹅中添加“飞”方法,相同的功能,重复代码明显不合理,也不利于扩展。
  • 加入我再新增一个“气球(balloon),再去继承父类就不合适了,我们需要一种规则,这种规则就是接口”
  • 老鹰、麻雀都会飞就实现飞这个接口,鸵鸟不会飞,就不实现这个接口
程序入口
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 接口的实现与继承 { class Program { static void Main(string[] args) { //实现多态 IFlyable[] flys = { new Sparrow(), new Eagle(),new Swan(), new Balloon() }; foreach (IFlyable outFly in flys) outFly.Fly(); Console.ReadLine(); } } }

【C#|10、接口、抽象、密封、开放封闭原则】接口声明
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 接口的实现与继承 { interface IFlyable { void Fly(); } }

抽象类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 接口的实现与继承 { abstract class Bird { public abstract void Eat(); } }

Eagle继承抽象类及接口
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 接口的实现与继承 { class Eagle:Bird,IFlyable { public void Fly() { Console.WriteLine("我是老鹰我会飞"); } public override void Eat() { Console.WriteLine("我是老鹰专吃小鸡"); } } }

Sparrow继承抽象类及接口
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 接口的实现与继承 { class Sparrow:Bird,IFlyable { public void Fly() { Console.WriteLine("我是麻雀我会飞"); } public override void Eat() { Console.WriteLine("我是麻雀,我吃粮食"); } } }

Sparrow继承抽象类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 接口的实现与继承 { class Ostrich:Bird { public override void Eat() { Console.WriteLine("我是鸵鸟,只吃草"); }} }

Swan扩展(继承抽象类及接口)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 接口的实现与继承 { class Swan:Bird,IFlyable { public override void Eat() { Console.WriteLine("我是天鹅,我吃鱼"); } public void Fly() { Console.WriteLine("我是天鹅我会费"); } } }

扩展:Balloon继承接口
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 接口的实现与继承 { class Balloon:IFlyable { public void Fly() { Console.WriteLine("我是气球我会飞"); } } }

显式实现接口
  • 隐式实现接口:即可用接口调用方法,也可用具体类调用方法
  • 显式实现接口:实现接口的方法前不能用访问修饰符public, 必须显式指定接口名称
    返回值类型 接口名称.接口方法
    只能是通过接口嗲用,而不能通过具体类调用
  • 同时隐式实现接口和显式实现接口:这样完成对弈个几口成员提供多份实现提,访问是可以永磊的实例和接口的引用来分别调用这两种实现实体。显式才是真正的接口实现方式
  • 结论:当显式实现方式存在时,隐式实现方式就失效了。但这不能标识显式实现方式就不好,当一个类实现的多个接口中具有相同的方法时,用显式方式来专门实现某个接口的方法时,就显得非常有用!
Main方法——接口调用
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _4_显示实现接口 { class Sparrow:Bird,Iflyable { public void Fly() { Console.WriteLine("(隐式实现)我是麻雀我会飞"); } void Iflyable.Fly() { Console.WriteLine("(显式实现)我是麻雀我会飞"); } public override void Eate() { Console.WriteLine("我是麻雀,我吃粮食"); } } }

接口1声明fly()方法
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _4_显示实现接口 { interface Iflyable { void Fly(); } }

接口2声明fly()方法
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _4_显示实现接口 { interface Iflyable { void Fly(); } }

抽象类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _4_显示实现接口 { abstract class Bird { public abstract void Eate(); } }

隐式实现
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _4_显示实现接口 { class Eagle:Bird,Iflyable { public override void Eate() { Console.WriteLine("我是老鹰,我吃小鸡"); } public void Fly() { Console.WriteLine("我是老鹰,我会飞"); } } }

显式实现
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _4_显示实现接口 { class Balloon:Iflyable,IFllyable2 { //显示实现接口 void Iflyable.Fly() { Console.WriteLine("我是Iflyable中的飞"); } void IFllyable2.Fly() { Console.WriteLine("我是IFllyable2中的飞"); } } }

隐式和显式同时存在
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _4_显示实现接口 { class Sparrow:Bird,Iflyable { public void Fly() { Console.WriteLine("(隐式实现)我是麻雀我会飞"); } void Iflyable.Fly() { Console.WriteLine("(显式实现)我是麻雀我会飞"); } public override void Eate() { Console.WriteLine("我是麻雀,我吃粮食"); } } }

抽象类与抽象方法声明 抽象类
  • 抽象类与非抽象类:抽象类前有关键字abstract, 没有则为非抽象类
  • 声明格式
public class abstract Fly
{
public abstract void Flys(); 抽象方法中没有方法体
}
  • 抽象类不能实例化,必须通过继承有派生类实现其抽象方法,因此不能new,不能sealed
  • 如果派生类没有实现所有的抽象方法,则派生类也必须声明为抽象类。
  • 如果一个非抽象类从抽象类中派生,则其必须通过重载来实现所有继承而来的抽象成员
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _5_抽象类与抽象方法 { public abstract class Class1 { //抽象类与抽象方法声明中必须包含abstract,并且抽象方法不能有方法体 //抽象类的声明就是为了实现派生或者继承,所以我们不能将它同时标记为sealed public abstract void Methond(); public abstract void Methond2(); //抽象类中可以包含非抽象方法 public void Add(int a, int b) { Console.WriteLine(a+b); }} }

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _5_抽象类与抽象方法 { public abstract class Class2:Class1 { //因为class1中有两个抽象方法,而class2中仅实现一个抽象方法,此时class2也必须声明为抽象类 public override void Methond() { throw new NotImplementedException(); } } }

抽象方法 定义:方法前abstract就被称为抽象方法,抽象方法不提供任何实际实现
注意:
抽象方法必须在抽象类中声明
不能使用static private virtual修饰符
方法不能有任何可执行程序,哪怕是方法体{}
重写抽象方法时采用override
实例:Pow类定义了一个求幂对象的抽象概念。Pow类方法时抽象的,PowB类和PowC类是Pow具体实现
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _5_抽象类与抽象方法 { class Program { static void Main(string[] args) { PowB myPowB = new PowB(); myPowB.PowMethond(2,10); PowC myPowC = new PowC(); myPowC.PowMethond(2,10); Console.ReadKey(); //abstract与virtual //共同点:都可以通过overriede来实现对原有方法的重写 //abstract没有自己的实现,virtual有自己的实现 } } }

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _5_抽象类与抽象方法 { public abstract class Pow { public abstract void PowMethond(int x, int y); } }

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _5_抽象类与抽象方法 { class PowB:Pow { public override void PowMethond(int x, int y) { int pow = 1; for (int i = 1; i <= y; i++) { pow *= x; } Console.WriteLine("求幂的结果是"+pow); } } }

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace _5_抽象类与抽象方法 { class PowC:Pow { public override void PowMethond(int x, int y) { Console.WriteLine("求幂的结果是"+ System.Math.Pow(x, y)); }} }

密封类与密封方法 密封类
  • 并不是所有的类都可以被继承,不能继承的类称为密封类,用关键字sealed
  • 使用场合:如果要对库,类或自己编写的其他类进行操作重写某些功能会导致编译错误,2)因为商业原因把类或方法边界为sealed,以发给第三方以违反注册协议的方式扩展该类
  • 密封类中不能包括虚方法(Virtual)和抽象方法(abstract),因为在密封的类中没有为派生类提供实现其虚方法和抽象方法的机会。
  • 在把类或方法标记为sealed时要小心,因为这么做会严重限制它的使用。即使不希望它能继承一个类或重写类的某个成员仍有可能将来的某时刻,有人会遇到没有预料的情形
  • .NET基类库大量使用了密封类,是希望从这些类中派生出自己的类的地方开发人员无法访问这些类,例如string就是一个密封类。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 密封类和密封方法 { //如果一个类不希望将来在使用过程被继承或被派生,这时候可以采用关键字sealed //虚方法virtual 抽象方法abstract存在就是为了派生,所以不能存在在密封类中。 sealed class Class2 { } }

密封方法 定义:用修饰符sealed进行标记的方法
作用:使用sealed修饰符可以以防止派生类进一步重写该方法
注意:如果实例方法声明包含sealed修饰符,则它必须也包含override修饰符
要在方法中使用sealed关键字,必须现在基类上将其声明为重写。如果基类上不希望有重写的方法或属性,就不要将其声明为virtual
密封类除了不能被继承外,其他与非密封类一样,密封方法则必须通过重写基类中的虚方法来实现
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 密封类和密封方法 { class Program { static void Main(string[] args) { Class2 mymethond = new Class2(); mymethond.Write(); Console.ReadKey(); } } }

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 密封类和密封方法 { public class Class1 { public virtual void Write() { Console.WriteLine("这是一个未密封的虚方法"); } } }

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 密封类和密封方法 { //如果一个类不希望将来在使用过程被继承或被派生,这时候可以采用关键字sealed //虚方法virtual 抽象方法abstract存在就是为了派生,所以不能存在在密封类中。 public class Class2:Class1 { public sealed override void Write() { Console.WriteLine("这是一个密封的方法"); } } }

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 密封类和密封方法 { class Class3:Class2 { // 不能重写密封方法 //public override sealed void Write() //{//} } }

本章小结 多态实现的方法:
虚方法:可以抽象出一个类,并且抽象出一个类需要有实现。例如员工 项目经理 工作计划(员工和项目经理都有工作计划,且项目经理属于员工,员工工作计划也要有实现)
抽象类:可以抽象出一个父类,但是抽象类需要有一个方法,但是实现方法的途径不确定(例如:老鹰,麻雀、鸵鸟都属于鸟,且都需要吃,但吃的东西不同,需要根据具体对象确定。所有鸟就是抽象类,抽象方法——方法没有具体实现(吃什么不知道)通过具体派生类实现具体方法——吃什么)
接口:不能抽象出一个父类,但是可找到他们共同的特点,也就是共同的行为能力(共同的属性、方法),接口表现的是一种能力一种规范
实例:真毛不会说话,Tom猫会说话,kitty猫会说话
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 本章小结及任务实施 { class Program { static void Main(string[] args) { ISayWords mycat1 = new TomCat(); mycat1.Say(); ISayWords myCat2 = new KittyCat(); myCat2.Say(); RealCat mycat3 = new RealCat(); mycat3.Say(); Console.ReadKey(); } } }

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 本章小结及任务实施 { interface ISayWords { void Say(); } }

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 本章小结及任务实施 { class RealCat { public void Say() { Console.WriteLine("我是真猫,我不会说话"); } } }

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 本章小结及任务实施 { class TomCat:ISayWords { public void Say() { Console.WriteLine("我是Tom猫,我会学别人说话"); } } }

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 本章小结及任务实施 { class KittyCat:ISayWords { public void Say() { Console.WriteLine("我是kitty猫,我自己会说话"); } } }

    推荐阅读