例如,IComparable 使得类型可以比较大小。接口是一种特殊的抽象类,在其中,不能显式地实现方法;也就是说,接口只拥有一组方法的签名。
不过,这个看似已经成为常识的事情在 C# 8 中可能会发生改变:接口也许可以拥有默认方法实现。如果这真的变成了现实,那么,C# 就成了一个允许多重继承的语言。
当然,不是继承了接口就万事大吉的,必须实现接口的方法才可以真正的扩展你的类。例如,必须实现 Compareto,并在其中解释如何比较大小。
接口负责对现实生活中 Has A 关系的描述。例如,可以比较大小明显是某个类拥有(has)的性质或能力,而不是说,某个类是“比较大小”,这在中文上都不通。
接口中的所有方法都是 public 的,也不能再使用其他访问修饰符进行修饰。接口也可以拥有自己的属性、事件和索引器。
显式接口实现如果两个不同的接口中含有一个相同的方法(相同的名称,参数和返回类型),那么,同时继承这两个接口的类型在实现该方法之后,两个接口的方法就会具有相同的行为:
class Program{ static voID Main(string[] args) { var cg = new Greeting(); var chinese = (ChineseGreeting)cg; //你好 chinese.Hello(); var english = (EnglishGreeting)cg; //你好 english.Hello(); Console.ReadKey(); }}public interface ChineseGreeting{ voID Hello();}public interface EnglishGreeting{ voID Hello();}public class Greeting : ChineseGreeting,EnglishGreeting{ //隐式接口实现导致方法只有一次实现机会 //两个接口的Hello将会有相同的行为 public voID Hello() { Console.Write("你好!"); }}为了区别开不同接口相同方法的实现,我们可以使用显式接口实现:
public class Greeting : ChineseGreeting,EnglishGreeting{ //显式接口实现 voID ChineseGreeting.Hello() { Console.Write("你好!"); } voID EnglishGreeting.Hello() { Console.Write("Hello!"); }}用的方式是相同的。在使用显式接口实现之后,必须将对象转化为接口类型才可以访问它。因为显式接口实现的方法默认是接口类型私有的(也不能使用访问修饰符),而且是 sealed。
下面的代码试图从接口的继承类型调用显式接口实现的方法,这会导致编译错误:
//继承类不存在此方法,显式接口实现方法归接口私有
cg.Hello();
下面是一个 .NET 中的显式接口实现的例子:
int 1=1;
//由于 ToSingle 是显式接口实现,所以必须先转化为接口基类型
var s = ((IConvertible) i).ToSingle(null);
float IConvertible.ToSingle(IFormatProvIDer provIDer);
ToSingle 签名包含了接口类型 IConvertible,说明了 ToSingle 方法使用了显式接口实现。显式接口实现与泛型接口在泛型还没出现的年代,实现一个接口有时是很头疼的。例如,常用的 IComparable 接口中的 Compareto 方法,输入参数是 object。
因此,任何输入都合法,这不仅可能带来装箱,还会在内部重新转化为自己想要的类型时发生错误。
显式接口实现可以在一定程度上避免这个问题:
public class People : IComparable{ public string name { get; set; } public int age { get; set; } //类型安全的Compareto方法 public int Compareto(People p) { return p.age >= age ? 1 : -1; } //为了实现ICompareable,不得不显示接口实现 int IComparable.Compareto(object obj) { //obj转化为People类型可能会出现问题 return Compareto((People)obj); }}上面的类型中,通过比较年龄来比较 People 类的大小。在接受 object 为参数的 Compareto 方法中,必须强制将 obj 转化为 People 类,而这是类型不安全的。
我们通过自己写一个 Compareto 方法,并令输入为 People 类解决这个问题。
但是自己的 Compareto 方法并不算是 IComparable 的实现,我们还要被迫实现一个 IComparable 规定的 Compareto 方法。
People p = new People();
People q = new People();
q.Compareto(p);
//如果对象是 People 类型,访问不到显式接口实现的 Compareto 方法
//也就不能传入任意类型
q.Compareto(i); //不能通过编译!
//但是可以通过将类型转化为接口基类型
//访问到显式接口实现的 Compareto 方法,传入任意类型
//于是再次失去类型安全性
var q2 = (IComparable) q;
q2.Compareto(i); //可以通过编译!
public class People : IComparable<People>{ public string name { get; set; } public int age { get; set; } //类型安全的Compareto方法 public int Compareto(People p) { return p.age >= age ? 1 : -1; }}继承 IComparable<T> 使得我们不需要再写一个输入参数为 object 的 Compareto 方法,它绝对保证类型安全,而且,由于没有类型转换,也就不存在装箱。
抽象类 VS 接口接口和抽象类有一些区别,接口是一种特殊的抽象类:
接口不能有非抽象的方法。而抽象类可以有非抽象的方法。接口和抽象类都不能被实例化。如果继承接口,必须实现接口所有的抽象方法。而继承抽象类的类可以只实现一部分父类的抽象方法。一个类可以继承多个接口,但只能继承自一个别的类。不能密封抽象类和接口,因为这破坏了接口和抽象类本身的性质,即被人继承。
抽象类和它的子类之间应该是一般和特殊的关系,而接口仅仅是它的子类应该实现的一组规则。当逻辑为一般和特殊的关系,而特殊本身无法或无需有任何方法的实现时,则考虑使用抽象类。
当逻辑为“能做什么”时,使用接口。这样的例子太多了,例如 C# 中所有的数据结构都继承接口 IEnumerable,意味着所有的@R_419_6722@都拥有枚举的能力。
另外,如果你的类型继承了接口 Idisposable,则拥有 dispose 的能力。
如果基类型发生变化(例如添加了方法),那么派生类型将自动继承该方法,不需要更改代码。
但如果接口添加了方法,那么所有派生类型都必须更改代码才能通过编译。在这种情形之下,要不就老老实也改代码,要不就考虑将新方法做成一个新的接口。
接口不继承自 Object接口不继承自任何类型,它旨在提供一个干净的方法库和固定的签名,并等待继承者来实现。
如果接口继承自 Object,那么它就会具有 Object 的方法了。但是,任何类型其实都已经继承了 Object,所以类型再继举一个继承了 Object 方法的接口,就会导致毫无意义的重复。
接口方法分派假设有一个接口 ITest,含有方法A,—个类型 B 继承了该接口,并实现了 B.A,那么,实际上 B.A 是被 IL 的 newslot+virtual 修饰的(显式接口实现的话,则再加一个 final 关键字)。
因此,可以在类型 B 的方法表中找到 B.A,然后进行调用。而相对的,虽然接口 ITest 是类型 B 的父类,而且方法 A 隐式的为 virtual,接口方法 ITest.A 是不会出现在类型 B 的方法表中的。 总结
以上是内存溢出为你收集整理的C# Interface:接口全部内容,希望文章能够帮你解决C# Interface:接口所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)