2012年7月3日星期二

读CLR via C#总结(12) 接口和基类

读CLR via C#总结(12) 接口和基类

  1,什么是接口?
  答:接口是表示一组函数成员(或称方法)而不实现成员的引用类型。这句话有两层意思。
    第一,接口包括一组函数成员但是不提供实现。
    第二,接口是引用类型。

  2,接口能为我们做什么?
  答:通过接口可以实现多继承。因为我们只能继承一个基类,但是我们可以继承多个接口。CLR通过接口提供了"缩水版"的多继承。

一,定义接口

  代码如下:

//定义接口    public interface IMyInterface1    {        void PrintOut(string s);//方法,最常用        string Name { get; set; }//属性    }

总结:1,接口定义使用关键字interface,并且接口名称要以大写字母I开头,目的是方便在源代码中辩认接口类型。
   2,接口定义类似于类型定义,接口可以在文件范围内定义,也可以嵌套在另一个类型中定义。比如可以在一个单独的cs文件中定义接口。
   3,接口定义可以有任何的访问修饰符,比如public,protected,internal或private。但是接口成员不能包括任何修饰符,因为接口成员是隐式public的。

二,使用接口

  以下代码定义了一个接口以及一个实现它的类,Main中的代码创建了类的对象,并通过类对象调用实现方法。它还创建了接口类型的变量,并强制把类对象的引用转换成接口类型的引用,并通过接口的引用来调用实现方法。

namespace InterfaceDemo1{    //定义接口    public interface IIfc1    {        void PrintOut(string s);    }    //定义类实现接口    class MyClass : IIfc1    {        public void PrintOut(string s)//为接口方法提供实现        {            Console.WriteLine("Calling through: {0}",s);        }    }    class Program    {        static void Main(string[] args)        {            MyClass mc = new MyClass();            mc.PrintOut("object");//通过类对象调用实现方法            //如果类实现了接口,表达式返回指向接口的引用.            //如果类没有实现接口,表达式返回null而不是抛出异常,这比使用强制转换运算符要好.            IIfc1 ifc = mc as IIfc1;//获取接口引用有两种方式,另一种是使用强制转换运算符如:IIfc1 ifc = (IIfc1)mc;                        if (ifc != null)            {                ifc.PrintOut("interface");//通过接口引用调用实现方法            }            Console.ReadKey();        }    }}

程序输出结果为:

三,泛型接口

  先看一下下面的代码:

private void SomeMethod1()        {            int x = 1, y = 2;            IComparable c = x;//获取接口引用,因为int实现了IComparable接口.            c.CompareTo(y);//y在这里装箱(因为CompareTo期望接受的参数类型是Object的)            c.CompareTo("2");//编译通过,运行报错.        }

这段代码中有以下两个问题。
第一,在处理值类型时经常装箱,造成性能损失。
第二,类型安全性低。在调用接口方法时,可传递对任何类型的实例的引用,不这是我们期望的。

解决这些问题最好的方式就是泛型接口,修改后的代码如下:

private void SomeMethod2()        {            int x = 1, y = 2;            IComparable<int> c = x;//获取接口引用            c.CompareTo(y);//y不会发生装箱(因为此时CompareTo期望接受的参数类型是int)            c.CompareTo("2");//编译错误(因为此时CompareTo期望接受的参数类型是int),保证了类型安全性.        }

所以总结泛型接口的优点有:

1,编译时的类型安全性。
2,处理值类型时,装箱次数减少。
3,类可以实现同一接口多次,只要每次使用不同的类型参数。

四,设计:基类还是接口?

  既然基类和接口都可以实现继承,那我们在设计的时候到底是使用基类呢还是使用接口?下面是总结的几条指导原则:

1,IS-A还是CAN-DO关系。首先判断派生类和基类的关系,如果是IS-A(属于)的关系,就使用基类实现继承。如果是CAN-DO(能做某事)的关系,就使用接口实现继承。
2,通过基类派生比较容易。因为基类提供的所有功能派生类只需要针对其行为稍做改动即可使用。而如果实现接口则必须实现其所有成员。
3,版本控制方面通过基类派生更好管理。如果向类型添加一个方法,派生类将直接继承新方法,代码不需要重新编译。而向接口添加一个成员时,会强迫接口的继承者修改源代码并要重新编译,不利于程序版本控制。


TAG: