通过理解List和IList的区别,加深对接口回调的理解

List是类,IList是接口,由于List在设计时继承了IList,所以在微软的PET SHOP4.0中会用以下形式来声明Module层的变量(有助于提高灵活性和多态的体现)。
IList<Module> module = new List<Module>();

 这让像我这样的OOP初学者产生了疑惑,产生一些很可笑的想法:是不是List和IList在声明的时候可以通用,他们是不是就是一回事?

这属于基本概念不清,没明白接口和类的关系、区别与作用。

接下来,我举个例子:

IList<string> aa = new List<string>();
List
<string> bb = new List<string>();
aa 
= bb;//通过
bb = aa;//通不过

 为什么会这样呢?因为List继承了IList接口, 所以aa = bb可以通过,反之则不行。

那么就有人问了:即然aa = bb可通过,那么我以后就全用IList<string> aa = new List<string>();的声明方式,不用List<string> bb = new List<string>();这样行吗?

如果你也有这行的疑问,我们再看一个例子:

    class dog : Ieat
    {
        
public void doeat()
        {
            
//吃东西
        }
    }

    
interface Ieat
    {
        
void doeat();
    }

 上面定义了一个eat接口,dog类继承了这个接口并实现了其doeat方法,接下来我们声明一下

//直接用类声明
dog d = new dog();
d.doeat();

//通过接口声明
Ieat d2 = new dog();
d2.doeat();

这时d和d2的作用是完全一样的(虽然他们两个的概念不同,但使用起来却是一模一样的),如果你认为就是你疑惑的地方所在,那么我们把例子修改一下,你就可以看出他们的不同了。

    class dog : Ieat
    {
        
public void doeat()
        {
            
//吃东西
        }

        
public void tail()
        {
            
//摇尾巴
        }
    }

    
interface Ieat
    {
        
void doeat();
    }

在上面的代码中我们在dog类中加入了一个新的动作tail(摇尾巴),这时之前的d和d2就不同了,d.tail();是可以使用的,而d2.tail();则无法使用,因为在Ieat接口中并没有tail方法。

即然声明Ieat可以使用的方法比声明dog类要少,那么声明时使用Ieat接口还有什么用呢?为了解决这个疑问,我们把代码再修改一下:

    class dog : Ieat
    {
        
public void doeat()
        {
            
//狗吃东西
        }

        
public void tail()
        {
            
//摇尾巴
        }
    }

    
class human : Ieat
    {
        
public void doeat()
        {
            
//人吃东西
        }

        
public void work()
        {
            
//工作
        }
    }

    
interface Ieat
    {
        
void doeat();
    }

 我们新加了一个human类也继承自Ieat接口,我们发现Ieat接口可以被dog类实例化,也可以被human类实例化:

//用dog类实例化Ieat
Ieat e = new dog();
e.doeat();

//用human类实例化Ieat
Ieat e2 = new human();
e2.doeat();

以上代码中的e和e2虽然都是Ieat类型的,但做的事却不同,这就是OOP中多态化的一种表现形式,其实在现实中我们不太会真的像以上代码这样直接去声明和实例化两个或更多个接口,而是写一个方法来实现多态调用(简单的工厂)。

        static void Main(string[] args)
        {
            eat(
new dog());//调用dog
            eat(new human());//调用human
        }

        
private void eat(Ieat e)
        {
            e.doeat();
        }

我们只需调用eat这个方法,根据传入的实例类形不同达到实现不同的功能。


上面我们从IList和List的区别开始引入,讲述了我今天下午学习接口回调的一些心得,可能很简单,但要理解却不容易,给自己留个记号。

原文地址:https://www.cnblogs.com/yeagen/p/2004701.html