C# Interface:接口

概述C# 的接口可以看成是一个技能库,继承一个接口可以看成是插上一个新的技能库,它使得你的类型拥有更多的技能。 例如,IComparable 使得类型可以比较大小。接口是一种特殊的抽象类, C# 的接口可以看成是一个“技能库”,继承一个接口可以看成是“插上一个新的技能库”,它使得你的类型拥有更多的“技能”。

例如,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();

显式接口实现有着诸多缺点,例如必须手动转换类型为接口基类型(如果派生类是值类型,那么还会导致装箱),没有 IDE 智能感知等。

下面是一个 .NET 中的显式接口实现的例子:

int 1=1;
//由于 ToSingle 是显式接口实现,所以必须先转化为接口基类型
var s = ((IConvertible) i).ToSingle(null);

直接在 i 上调用 ToSingle 无法通过编译。我们看看 int 中的相关代码:

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:接口所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1235025.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-06
下一篇 2022-06-06

发表评论

登录后才能评论

评论列表(0条)

保存