接口、索引器、Foreach的本质(学习笔记)
接口
什么是接口?
接口代表一种能力,和抽象类类似但比抽象类的抽象程度更高!
接口的定义:
接口的定义1 public interface IEat//定义一个接口 2 { 3 void Eat(string food);//为该接口定义一种能力 4 }
从上边的例子中我们可以看到,接口中的方法是没有方法体的甚至连访问修饰符都没有。而且在接口中只能有方法、属性、索引器及事件!
接口的使用:
1
public class
Dog:IEat //Dog类实现IEat接口 2 { 3 //Dog类实现IEat接口的Eat方法 4 public void Eat(string food) 5 { 6 Console.WriteLine("吃"+food); 7 } 8 }
注意:如果一个类实现了一个接口则该类必须实现该接口及其父接口的所用方法除非该类是一个抽象类则交由该类的子类来实现!一个接口也可以继承另一个接口而不实现该接口的方法而是交由其实现者来实现
Demo:
Mode1 //接口一 2 public Interface IEat 3 { 4 void Eat(); 5 } 6 //接口二 7 public Interface Ipaly 8 { 9 void Play(); 10 } 11 //接口三 12 public Interface Itravel:IEat,Ipaly 13 { 14 //接口三继承接口一和接口二 15 } 16 17 public class program:Itravel 18 { 19 public void Eat() 20 { 21 //省略方法体 22 } 23 public void Play() 24 { 25 //省略方法体 26 } 27 }从上面的demo中一些可以看到Itravel接口继承的IEat接口和Ipaly接口可能有些朋友要问了”在C#以及JAVA中不是不支持多继承吗?”
注意这句活是针对类而言的,但针对接口来说一个类是可以实现多个接口的同样一个接口也可以继承多个接口
接口和抽象类的区别:
当需要的各个对象之间存在父子类关系时,可以考虑使用抽象类,
当各个对象之间不存在继承关系,只是有相同的能力时,而已考虑使用接口
抽象类是某一类事物的抽象结果而接口则代表某些事物的一种能力
接口的注意点:
01.接口就是为了约束方法的格式(参数和返回值类型)而存在的
02.接口可以实现多继承,弥补单继承的缺陷。
03.接口可以看成是一个特殊的抽象类,通过反编译看源码可知
04.接口中方法不用访问修饰符,因为CLR会自动添加,并且不能有方法体
05.如果一个类实现了某个接口,就得实现该接口中所有的方法
06.接口要谨慎使用,防止出现接口污染!
07.接口仅仅代表一种能力,实现该接口的类和接口没有继承关系
08.接口是用来实现的,类是用来继承的。
09.其实很多时候,看似可以不用接口,因为接口就是一个方法的约定,
10.抽象类也可以实现接口,但接口成员必须映射到抽象类的抽象成员。抽象类的派生类如果是非抽象类,则必须通过方法重载来实现接口成员
索引器
为什么要使用索引器?
解析:当一个类中包含数组或集合成员时使用索引器可以大大简化度数组或集合成员的存取操作。
索引器的定义
索引器的定义和属性类似
索引器的定义语法1 /*[修饰符] 数据类型 this[索引类型 index] 2 { 3 get{//获得属性的代码} 4 set{ //设置属性的代码} 5 } 6 */
demo:
1 2 public class Students 3 { 4 string[] names = new string[3]; 5 public string this[int index] { 6 get{return names[index];} 7 set{names[index]=value;} 8 } 9 int[] age = new int[3]; 10 public int this[string Key] 11 { 12 13 get { 14 for (int i = 0; i < names.Length;i++ ) 15 { 16 if (Key.Equals(names[i])) 17 { 18 return age[i]; 19 } 20 } 21 return -1; 22 23 } 24 set 25 { 26 for (int i = 0; i < names.Length; i++) 27 { 28 if (Key.Equals(names[i])) 29 { 30 age[i]=value; 31 } 32 } 33 ; 34 } 35 } 36 }
Foreach的本质:
为什么数组和集合可以用Foreach遍历?
解析:因为数组和集合都实现了IEnumerable接口,该接口只有一个方法GetEnumerator()所以只要实现了IEnumerable接口的类都可以被Foreach遍历
IEnumerable原型1 // 摘要: 2 // 公开枚举数,该枚举数支持在非泛型集合上进行简单迭代。 3 [ComVisible(true)] 4 [Guid("496B0ABE-CDEE-11d3-88E8-00902754C43A")] 5 public interface IEnumerable 6 { 7 // 摘要: 8 // 返回一个循环访问集合的枚举数。 9 // 10 // 返回结果: 11 // 一个可用于循环访问集合的 System.Collections.IEnumerator 对象。 12 [DispId(-4)] 13 IEnumerator GetEnumerator(); 14 }
从上面代码中我们可以看到IEnumerable接口的GetEnumerator()方法返回一个实现了IEnumerator接口的对象!
IEnumerator原型1 // 摘要: 2 // 支持对非泛型集合的简单迭代。 3 [ComVisible(true)] 4 [Guid("496B0ABF-CDEE-11d3-88E8-00902754C43A")] 5 public interface IEnumerator 6 { 7 // 摘要: 8 // 获取集合中的当前元素。 9 // 10 // 返回结果: 11 // 集合中的当前元素。 12 object Current { get; } 13 14 // 摘要: 15 // 将枚举数推进到集合的下一个元素。 16 // 17 // 返回结果: 18 // 如果枚举数成功地推进到下一个元素,则为 true;如果枚举数越过集合的结尾,则为 false。 19 // 20 // 异常: 21 // System.InvalidOperationException: 22 // 在创建了枚举数后集合被修改了。 23 bool MoveNext(); 24 // 25 // 摘要: 26 // 将枚举数设置为其初始位置,该位置位于集合中第一个元素之前。 27 // 28 // 异常: 29 // System.InvalidOperationException: 30 // 在创建了枚举数后集合被修改了。 31 void Reset(); 32 }当我们使用Foreach遍历一个对象时会先执行该队形的GetIEnumerable方法得到一个迭代器(实现的IEnumerator接口的对象),然后调用给对象的Reset方法来初始枚举数,然后执行该对象的MoveNext()方法得到Bool值如果该方法返回真则调用该对象的Current属性得到值在执行MoveNext()方法直到该方法返回假则退出迭代
Demo:
自定义可供foreach遍历的类1 //可供Foreach遍历的类(实现了IEnmerable接口) 2 public class MyList:IEnumerable 3 { 4 ArrayList list = new ArrayList(); 5 public void Add(object o) 6 { 7 list.Add(o); 8 } 9 public IEnumerator GetEnumerator() 10 { 11 return new MyIenumerator(list); 12 } 13 } 14 //实现了IEnumerator接口的类 15 public class MyIenumerator:IEnumerator 16 { 17 public MyIenumerator(ArrayList list) 18 { 19 this.list = list; 20 } 21 ArrayList list = new ArrayList(); 22 int I = -1; 23 public object Current 24 { 25 get { return list[I]; } 26 } 27 28 public bool MoveNext() 29 { 30 bool falg = false; 31 if (I<list.Count-1) 32 { 33 I++; 34 falg = true; 35 } 36 return falg; 37 } 38 39 public void Reset() 40 { 41 42 } 43 }