C#接口和抽象类的总结

来源:互联网 发布:linux中samba干嘛用 编辑:程序博客网 时间:2024/05/20 13:38

一、接口的简单实现

接口更好地实现了多态,体现了约定和实现相分离的原则。通过面向接口编程,可以降低代码间的耦合性,提高代码的可扩展性和可维护性。在面向对象编程中,提倡面向接口编程,而不是面向实现编程。面向接口编程意味着,开发系统时,主体架构使用接口,接口构成系统的骨架,这样就可以通过更换实现接口的类来实现更换系统。一个类实现的某个接口,就表示这个类具有了某种能力,这个能力具体体现在实现接口的方法上。

如下代码,有一个Duck父类,它有三个子类,RealDuck、WoodDuck、RubberDuck。其中RealDuck和RubberDuck会叫,只是叫声不一样,WoodDuck不会叫。有一个鸭子叫的接口IBark。

/// <summary>    /// 鸭类    /// </summary>    public class Duck    {        public void Swin()   //游泳方法        {            Console.WriteLine("鸭子在水中游...");        }    }    //鸭子叫的接口    public interface IBark    {        void Bark();    }    /// <summary>    /// 真实的鸭子    /// </summary>    public class RealDuck : Duck, IBark    {        public void Bark()        {            Console.WriteLine("嘎嘎叫...");        }    }    /// <summary>    /// 橡皮鸭    /// </summary>    public class RubberDuck : Duck, IBark    {        public void Bark()        {            Console.WriteLine("唧唧叫...");        }    }    /// <summary>    /// 木头鸭    /// </summary>    public class WoodDuck : Duck    {    }
在Main()方法中,当我们想要让鸭子叫时,不能用父类变量指向会叫的鸭子对象,因为这样会让这个变量无法调用子类鸭子叫的方法,而是应该用接口来声明这个变量。代码如下:
static void Main(string[] args)        {            //Duck duck = new RealDuck();                        IBark bark = new RealDuck();            bark.Bark();            Console.ReadKey();        }
这样在控制台上显示的结果是“嘎嘎叫...”。

二、显式实现接口

先看如下示例代码,一个具有飞行功能的接口IFlyable,一个可以飞行的超人接口ISuperman,这两个接口里面有一个同名的飞行方法Fly,另外有一个学生类同时继承了这两个接口,并且实现了接口所具有的Fly()方法,一个是直接实现,一个是显式实现。

//具有飞行功能的接口    public interface IFlyable    {        void Fly();    }    //具有飞行功能的超人接口    public interface ISuperman    {        void Fly();    }    public class Student : IFlyable,ISuperman    {        public void Fly()        {            Console.WriteLine("IFlyable接口中的Fly方法");        }        void ISuperman.Fly()        {            Console.WriteLine("ISuperman接口中的Fly方法(显示实现的方法)");        }    }
在Main方法中,我们让三个不同类型的变量分别指向一个学生对象,如下:
static void Main(string[] args)        {            IFlyable fly = new Student();  //飞行类            fly.Fly();            ISuperman man = new Student();  //超人类            man.Fly();            Student stu = new Student();  //学生类            stu.Fly();            Console.ReadKey();        }
运行程序,结果如图所示:

从上面可以看出,显式实现的Fly方法的对象只有用实现该显式方法的接口来声明,我们才可以调用这个显式方法。在上面的代码中,你会发现,显式实现的方法前面没有访问修饰符,也就是默认为private,而直接实现的方法的访问修饰符是public。如果在显式实现的方法前面加一个public访问修饰符,那么程序就会报错,如图所示:


错误信息提示我们:修饰符public对该项无效。这也告诉我们,显式实现接口的方法必须为private。既然这个方法是私有的,那在外界就不能访问了,那我们用ISuperman类型的变量指向的Student对象为什么又可以访问到呢?因为这个对象访问的是ISuperman接口中的Fly方法,而所有接口中的方法都默认必须为public,并且继承这个接口的Student类已经显式的实现了这个Fly方法,所以可以访问到。显式实现接口的目的,就是为了解决方法重名的问题。

假如我们把Student类中直接实现的IFlyable接口的方法删掉,然后让这个接口也显式实现其方法,如下所示:

public class Student : IFlyable,ISuperman    {        //public void Fly()        //{        //    Console.WriteLine("IFlyable接口中的Fly方法");        //}        void ISuperman.Fly()   //显式实现ISuperman接口的方法        {            Console.WriteLine("ISuperman接口中的Fly方法(显示实现的方法)");        }        void IFlyable.Fly()   //显式实现IFlyable接口的方法        {            throw new NotImplementedException();        }    }
那么,在Main方法中,我们会发现程序报错,如图:

我们会发现,用Student类声明的对象无法访问到Fly这个方法,因为这个方法是私有的(private),外界声明的Student类型的对象无法访问。所以,我们可以得出结论,显式实现的方法必须要由实现显式方法的接口声明的对象调用。


三、接口特点的总结:

1.接口是一种规范,是为了更好的实现多态。

2.接口不能被实例化。

3.接口中的成员不能加访问修饰符。因为接口中的成员的访问修饰符默认就为public,不能修改。

4.接口中的成员不能有任何实现(也就是光说不做,只是定义了一组未实现的成员)。

5.接口中只能有方法、属性、索引器、事件,不能有字段。

6.接口与接口之间可以继承,并且可以多继承。

7.实现接口的子类必须实现该接口的全部成员。

8.一个类可以同时继承一个类并实现多个接口,如果一个子类同时继承了父类A,并且实现了接口IA,那么语法上A必须写在IA的前面,因为类是单继承的。

9.当一个抽象类实现接口的时候,如果不想把接口中的成员实现,可以把该成员实现为abstract。

10.显式实现接口,只能通过接口变量来调用,因为显式实现接口后,成员必须为private(上面已有示例)。


四、使用接口的建议:

面向抽象编程,我们提倡使用抽象(父类、抽象类、接口)而不使用具体,我们在编程时:

1.在定义方法返回值、参数、声明变量的时候能使用抽象就不要用具体。这个顺序一般为:接口→抽象类→父类→具体类。

2.能使用接口就不用抽象类,能用抽象类就不使用父类,能用父类就不使用子类。

3.避免定义体积庞大的接口和多功能接口,这样会造成接口污染。只需要把相关联的一组成员定义到一个接口中,尽量在接口中少定义成员,这就是单一职责原则。

4.我们一般应定义多个职责单一的接口来组合使用。


五、抽象类的特点

1.不能被实例化,需要被继承,这是为了实现多态。

2.子类必须重写父类所有的抽象成员,除非子类也是一个抽象类。

3.抽象成员在父类中不能的任何实现。

4.抽象类中可以有实例成员。

5.抽象成员的访问修饰符不能是private。

6.抽象成员只能写在抽象类中。


六、抽象类和接口的区别

1.抽象类适用于同一系列(类型),并且有需要继承的成员。

2.接口适用于不同系列的类具有相同的动作(行为、动作、方法)。

3.对于不是相同的系列,但具有相同的行为时,就应该要使用接口。

4.接口解决了类不能多继承的问题。





原创粉丝点击