(*) 接口也可以继承接口,但子接口不提供父接口方法的实现
(*) 当一个类同时继承父类又实现接口,要把接口放在最后面,如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);
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架