首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网站开发 > asp.net >

IList跟List根本区别

2013-09-06 
IList和List根本区别IListTListTIListT listnew ListT()知道IList是接口,List是一个实现IList的

IList和List根本区别
IList<T>
List<T>
IList<T> list=new List<T>();
知道IList是接口,List是一个实现IList的类,List可以隐式转换为IList
但是感觉这样还没有真正理解他们的区别,心里面还有疑问:
1、List实现了IList的哪些方法?是实体类T里面的字段?
2、IList既然是个接口,它为什么可以充当数据源?
3、IList<T> list=new List<T>();这样转化的用意是什么?
[解决办法]
1不知道怎么回答你,
2/3可以一并回答,IList<T> list=new List<T>();得到的是 List<T>的实体,所以可以做数据源,这样写的用意就是初始化成实现类List<T>的一个实体,貌似没什么其他特别的
[解决办法]
这些问题和 IList 或者 List 无关,表明lz的基础知识不扎实,对接口和泛型的理解不到位。

这三个问题本身问的都不对
1、List实现了IList的哪些方法?是实体类T里面的字段?
List还是IList泛型和T无关,T也不是实体类。
难道你没有见过 List<int> 或者 List<string> 么?

2、IList既然是个接口,它为什么可以充当数据源?
IList没有办法充当数据源,而是实现了 IList 接口的类的实例对象充当数据源。

3、IList<T> list=new List<T>();这样转化的用意是什么?
这里没有任何转化。
[解决办法]
C#高级编程第6版第108页,找到“把引用变量声明为接口引用的方式,这表示它可以指向实现这个接口的任何类的实例。但我们只能通过这些引用调用接口的方法--如果要调用由类执行的、不在接口中的方法,就需要把引用强制转换为合适的类型。接口引用完全可以看做是类引用--但接口引用的强大之处在于,它可以引用任何实现该接口的类。”

[解决办法]
至于说给变量赋值,那其实很简单,不要想太复杂了。比如说

A a=b;
这就是把b变量引用的对象同时也让变量a来引用。编译器回去检查b变量声明时的类型是否兼容于A,例如A是其父类、其具有的接口。假设你这样声明了,那么编译器也就放过它了。

比如说我写
string b = "asdf";
int a = b;
这显然会被编译器挑出来bug。这是因为编译器知道int跟string不兼容。

同样地,编译器知道你的赋值表达式右边的变量是 List<T>类型,左边的变量是IList<T>类型,于是编译就通过了,就允许左边的变量也去引用右边的变量所引用的对象了。
[解决办法]
所谓的“List<T>可以隐式转换为IList<T>”,其实运行时什么都不用做,就是正常地去让左边的变量引用右边的变量所引用的对象就行了。这里不要纠结“转换”这个词。这只是变量声明上面有差别,而两个变量所引用的对象没有差别,具体对象其实没有做任何转换。

一个对象可以有很多的“侧面”,就好象张柏芝是一个人,也是一个女人,也是一个母亲,也是一个离婚女人,也是一个名人,也是一个演员,等等。她有很多个侧面,其中“女人”也就是一个“人”,一个“母亲”或者一个“离婚女人”也就是一个“女人”,一个演员也就是一个人,等等,因此我可以只说很具体的类型而不说抽象的类型,我说她是一个名人的时候你肯定知道她也是一个人而不是一个动物。

把List<T>的对象引用赋值给IList<T>的变量也是这样,它们是兼容的。就好象我说一个是名人,你就知道他也是人,而反之则不一定成立。

程序允许你以多个变量、以多态的方式来引用对象。这是很基本的编程概念。同时编译器要尽可能多地检查出来类型不兼容的bug,让bug出现在编译时期,而不是在运行时才崩溃。

对于使用一个IList<T>对象作为数据源的宿主程序来说,它使用传递给他的对象的这个“侧面”上的功能来操作。就好象某人跟张柏芝交往时只是使用其作为女人的这个侧面,而不关心她作为演员这个侧面。这就是面向接口编程,就是去目标对象上去找它为了这个接口而定义的功能方法的入口,而不关心它其它的接口定义(在List<T>上有而在IList<T>上没有的那些)的功能入口。



你有个空间想象力,就更容易理解。不过核心的概念只有一个:对象是独立和唯一的,不会因为引用它的变量在定义时声明类型的不同而改变自己。你的许多误区都是在于纠结于引用对象的变量的类型声明和操作上,把这些表面文章误认为是对象自己在变来变去、有分身法可以复制出各种拷贝,这其实是把问题严重地复杂化了!
[解决办法]
我有些想起我们小学学的文书格式。比如,请假条有一个固定格式:
起行是称呼,然后是正文,最后是落款和日期。

至于向谁请假,请假内容是什么,谁请的假都没有指定,因为那必须到最终请假人来决定。

这就好比请假条的固定格式就是接口,真正具体的请假条就是实现的类。
当然这个比喻不是太恰当,因为这个比喻最恰当的是来比喻类和实例更贴切。

再举一个例子,所有人都知道,毛笔是用来写字的,扫帚是来扫地的,但又有谁规定了扫帚就不能写字了?
我现在需要一个只要能写字都可以的工具。
比如WriteSomthing(Tool)

于是就有一个问题了,这个Tool到底是毛笔呢还是扫帚???
显然毛笔和扫帚不是一类的东西,也不太好用继承的关系来做,如果可以的话,到底是毛笔继承扫帚还是扫帚继承毛笔呢?抑或是扫帚和毛笔共同继承一个有毛的东西?那没毛的扫帚咋办呢?
因此,最好的办法就是提取他们共有的功能出来——写作为一个合约,这个合约只是规定你需要有这个功能,至于你怎么去实现这个功能我不管,但是你的提供这个功能,那么这个合约就叫接口了,因此可以设计一个IWrite的接口,这个接口规定必须实现的一个功能是Write。
现在好了,Tool就可以指定为IWrite,意思就是只要实现了IWrite指定的功能一切工具管你是毛笔、扫帚还是树枝石头啥的,都可以拿来用。
因此WriteSomething(IWrite Tool)就是我们想要的东西
不过还有需要注意的一点,传递进来的参数必须是实现了IWrite功能的任意类的实例。

我不知道这样说有没有人明白?

[解决办法]
首先要说明一下,接口和泛型没什么关系。或者,用比较正式的说法,这两者是正交的。

对于接口来说,它的意义在于声明,告诉程序,实现它的对象可以干些什么。

例如,有一个“I储蓄所”的接口,有两个方法“存款”和“取款”,告诉我们一个“储蓄所”可以干些什么。
XX银行有它自己的“XX银行储蓄所”的类,实现“I储蓄所”接口,意味着所有“XX银行储蓄所”都会提供“存款”和“取款”功能。此外,它可能还规定了装修风格、服务方式之类的内容。
OO 街上有一个“XX银行储蓄所”的实例,你可能从未听说过“XX银行”,但你发现它实现了“I储蓄所”这个接口,于是你知道你可以在这里“存款”或“取款”。

至于泛型,假设我们的“I储蓄所”只能接受一种货币的存取,但不同的国家货币不同,那么可以将“I储蓄所”接受的货币作为泛型参数“T货币”。
假设“XX银行”是个跨国银行,它的储蓄所类其实是一个泛型类“XX银行储蓄所<T货币>”,它在中国的储蓄所都只接受人民币(是“XX银行储蓄所<人民币>”的实例),在美国的储蓄所都只接受美元(是“XX银行储蓄所<美元>”的实例),除此之外完全一样。

因此,楼主的问题:

1、List实现了IList的哪些方法?是实体类T里面的字段?
  List<T>了IList<T>的所有方法(如 Add, Remove 等等),它只接受 T 类型的数据,它不在乎 T 中究竟有些什么内容。
  (对比:“XX银行储蓄所<T货币>”实现了“I储蓄所<T货币>”的所有方法(“存款”和“取款”),只要泛型参数是货币,不在乎货币之外的信息)
2、IList既然是个接口,它为什么可以充当数据源?
  这个之前有人说过了,充当数据源的是实现了 IList 的对象(实现了 IList 的类的实例)
  (对比:实现了“I储蓄所”接口的机构可以提供存取款功能)
3、IList<T> list=new List<T>();这样转化的用意是什么?
  你得到了一个实现了 IList 的对象 list,你可以对它进行 IList 中声明了的操作,而不在乎它究竟是什么。
  (对比:你在去“XX银行OO街储蓄所”的路上遇到一个熟人,对方问你去干什么,你告诉他你去银行,于是他知道你去存款或取款,但不在乎你究竟去哪个储蓄所)

[解决办法]

引用:
知道IList是接口,List是一个实现IList的类,List可以隐式转换为IList
但是感觉这样还没有真正理解他们的区别,心里面还有疑问:
1、List实现了IList的哪些方法?是实体类T里面的字段?
2、IList既然是个接口,它为什么可以充当数据源?
3、IList<T> list=new List<T>();这样转化的用意是什么?

1.实现了IList接口所有方法,<T>,这是泛化类型,和类,接口的没联系。
2.看msdn
3.这是为了统一代码,这样类比一下:
动物 a = new 人();
a.Run();//这样行不行呢?不行,这是一个很烂的设计,不是所有动物都可以run


所以,我某个地方,只需要能跑的,我更关注的是这个行为是否具有,所以,可以写接口:
public interface IRunAble
{
    void Run();
}
然后可以跑的去实现这个接口
public class 人 : 动物 , IRunAble
{
    public void Run(){}
}
调用的时候,只想写通用的跑步相关的操作,不一定是人,任何实现该接口的都可以,我设计时候只考虑是否可以跑,不考虑是否是人,狗,猫都可以,或是未来的物种都可能,只要能跑。所以可以写为:
IRunAble r = new 人();
r.Run();

这样是可以的。

那么这样不是也写死了么?是的,所以,设计模式的工厂模式就用在这里了。
扩展一下

IRunAble r = MyFactory.GetRunner();
r.Run();

然后,以上代码,或相关跑步的代码未来都不用修改了,你只需要配置你的工厂,实现新的类继承IRunAble就可以了。

例如
public class MyFactory
{
    public static IRunAble GetRunner()
    {
        //暂时只返回人,回头你可以根据条件,构造不同的实现IRunAble的对象。
        return new 人();
    }
}

热点排行