(*) 接口也可以继承接口,但子接口不提供父接口方法的实现
(*) 当一个类同时继承父类又实现接口,要把接口放在最后面,如class xxx : ParentClass, ISomeInterface
(*) 当同时实现的多个接口中出现同名方法
除了解决同名方法冲突,此段代码同时展示了3种使用接口的方式:
{
void Draw();
}
public interface IDraw3D
{
void Draw();
}
public class Image : IDraw, IDraw3D
{
// public void IDraw.Draw() Error! 加任何访问修饰符都会有编译错误
void IDraw.Draw() // 用指定接口名来解决命名冲突,记住这样不能加访问修饰符
{
Console.WriteLine( " IDraw " );
}
void IDraw3D.Draw()
{
Console.WriteLine( " IDraw 3D " );
}
}
class Program
{
static void Main( string [] args)
{
Image image = new Image();
// 方法1
try
{
((IDraw)image).Draw();
}
catch (InvalidCastException ex)
{
Console.WriteLine( " Wrong type! " );
}
// 方法2
IDraw draw = image as IDraw;
if (draw != null )
{
draw.Draw();
}
else
{
Console.WriteLine( " Wrong type! " );
}
// 方法3
if (image is IDraw)
{
draw.Draw();
}
else
{
Console.WriteLine( " Wrong type! " );
}
}
}
(*) 自动生成代码
实现接口的类要写不少代码,而且还经常会碰到上面说的语法问题,所以VS提供了一种自动生成代码的方式。
当写好一个类的框架后,如下:
public class Image : IDraw, IDraw3D
{}
把鼠标放在接口上,然后就不说了吧。注意有两种,一种是普通的implement,另一种是explicit implement(即带接口名且没有访问修饰符的)。
(*) 常用接口
(*) IEnumerable
实现IEnumerable接口才能使用foreach,实现IEnumerable接口只需要实现一个GetEnumerator方法。
public interface IEnumable
{
IEnumerator GetEnumerator();
}
注意到GetEnumerator返回一个IEnumerator,这又是一个接口,定义如下:
{
bool MoveNext(); // 还有下一个则前往下一个并返回ture,否则返回false
object Current { get ; } // readonly property,这也是为什么foreach不能改写元素
void Reset(); // 不同于C++的迭代器,reset之后不是指向第一个元素,而是第一个元素之前
}
在实际应用中往往用不着去实现所有这些,因为很多集合例如Array已经实现好了这些接口,
比较常见的一种用法:
IEnumerable在命名空间System.Collections里。
class MyItem
{
//
}
class MyList : IEnumerable
{
private MyItem[] myArray;
#region IEnumerable Members
public IEnumerator GetEnumerator()
{
return myArray.GetEnumerator();
}
#endregion
}
也可用yield来实现IEnumerable接口。看代码:
{
string [] m_Days = { " Sun " , " Mon " , " Tue " , " Wed " , " Thr " , " Fri " , " Sat " };
#region IEnumerable Members
public IEnumerator GetEnumerator()
{
yield return m_Days[ 2 ];
yield return m_Days[ 4 ];
yield return m_Days[ 6 ];
yield break ;
}
#endregion
}
class TestDaysOfTheWeek
{
static void Main()
{
DaysOfWeek week = new DaysOfWeek();
foreach ( string day in week)
{
System.Console.WriteLine(day);
}
IEnumerator myEnumerator = week.GetEnumerator();
while (myEnumerator.MoveNext() == true )
{
System.Console.WriteLine(myEnumerator.Current);
}
}
}
yield是很了不起的,它像是一个状态机,记录着迭代器中当前的位置。下面这段关于yield的话是我从别处抄来的,写的似乎很有道理:
1。代码很简洁。其实这里多了一个yield return 语句,由于yield return并不对应多余的il指令。所以编译器就会在编译的时候,生成一个实现Ienumator接口的类.并且自动维护该类的状态.比如movenext,
2. 使用yield return 很容易实现递归调用中的迭代器. 如果以上的问题,不使用yield return的话,可想而知.要么你先把所有的结果暂时放到一个对象集合中. 可是这样就以为着在迭代之前一定要计算号. 要么可能你的movenext 就相当的复杂了. .NET 编译生成的代码其实利用了state machine. 代码量也很大.
类似迭代的调用,比如二叉树遍历 用yield return 就很方便了.另外还有常说的pipeline模式也很方便了.
可是yield return 还是有一些缺陷.
比如:如果有一个参数是ref 或者 out, 那这个state machine就很难去维护状态了. 事实上,yield不支持方法带有ref或者out参数的情况. 还有很多它也不支持,例如unsafe,catch等等,详见MSDN.
(*) ICloneable
只要实现一个Clone方法就行了。这个有点儿像C++中自定义的拷贝构造函数,例如我们不想让多个引用同时指向一个资源,就可以在拷贝构造函数中新申请一个资源。也就是所谓的深拷贝。
需要稍微注意的一点:Clone的signature是
object Clone();
所以在调用Clone的时候经常伴随转型操作,例如:Point p2 = (Point)p1.Clone();
一个深拷贝的例子:
public object Clone()
{
Point newPoint = (Point)this.MemberwiseClone(); // MemberwiseClone是object都有的protected方法
// TODO: 其他需要深拷贝的操作
}
(*) IComparable
和C标准库里的binary_search的钩子函数一个道理。
public interface IComparable
{
int CompareTo(object obj); // this在obj前面(也可以说this比obj小)返回负数,this在obj后面返回正数,相等返回0
}
实现了CompareTo方法之后,就可以用Array的静态方法Array.Sort来排序了。看下面代码:
{
string name;
int score;
public Student( string name, int score)
{
this .name = name;
this .score = score;
}
public override string ToString()
{
return String.Format( " {0} {1} " , name, score);
}
#region IComparable Members
public int CompareTo( object obj)
{
Student temp = (Student)obj;
// 谁的分高谁在前
if ( this .score > temp.score)
{
return - 1 ;
}
else if ( this .score < temp.score)
{
return 1 ;
}
else
{
return 0 ;
}
}
#endregion
}
class Program
{
static void Main()
{
Student stu1 = new Student( " Bill " , 75 );
Student stu2 = new Student( " Steve " , 85 );
// Console.WriteLine(stu1 > stu2); 实现了CompareTo函数也不能直接用>和<来比较
Console.WriteLine(stu1.CompareTo(stu2)); // 若stu1比stu2靠后,则返回正数
Student[] group = new Student[ 2 ];
group[ 0 ] = stu1;
group[ 1 ] = stu2;
Array.Sort(group);
foreach (Student stu in group)
{
Console.WriteLine(stu);
}
}
}
Array.Sort还提供了重载,即允许把排序准则当参数传入。
interface IComparer
{
int Compare(object o1, object o2);
}
//实现IComparer接口的类
public class NameComparer : IComparer
{
// 按名字排序
}
//调用
Array.Sort(group, new NameComparer);