C# Interface的使用方法探讨

  接口是把公共实例(非静态)的方法和属性结合起来,以封装特定功能的一个集合,一旦定义了接口,就能够在类中使用实现接口中的全部成员, 接口能够看做建立者和使用者之间的契约,一旦实现了接口,就不要轻易变更(若是须要变动接口,通常须要继承旧接口而且添加版本号)。咱们知道在C++里面是有纯虚函数,虚继承和多重继承的,C#里面为了简化C++的这些复杂的设施,引出了接口这个概念。
 
C#接口和类的区别:
1. 不容许使用访问修饰符(public, private, protected,或者internal)修饰接口成员,全部的接口成员都是公共的。
2. 接口成员不能包含代码体
3. 接口不能定义字段成员
4. 不能用关键字static,virtual,abstract或者sealed来定义接口成员
5. 类型定义成员是禁止的。
 
 
实现C#隐式接口:
  其实接口和C++中那种头文件声明一个接口而后在cpp里面实现一遍那种作法看上去没有什么区别,只是C#把这个作的更纯粹(自从学了C#我愈加以为C++真是一门很啰嗦的语言)。若是一个类继承了一个接口,那么其对接口内的内容的实现能够在当前类实现,也能够在当前类的基类实现:
public class FuckBase { public void FuckSomething(int fuck) { } } public class Fuck :FuckBase, A { public int AInt { get; private set; } public void DoSomething() { } }

 

  好比上面这个例子,就在基类中实现了接口,若是要隐藏基类的接口,能够直接new一下。
  固然了,接口是能够继承的,好比:
public interface A { void DoSomething(); } public interface DeriveedA: A { new void DoSomething(); }
  
  在C#的接口中能够定义属性,好比:
public interface DeriveedA: A { new void DoSomething(); int AInt { get; set; } }
  
  这样定义了之后,继承了DeriveedA的类必须实现AInt的get和set两个属性访问器了,而且都必须是public,有时候咱们须要下降写访问器的访问权限,咱们能够不在接口中定义set属性访问器,这样咱们能够在类中实现有特殊访问属性的set属性访问器了,好比:
public interface DeriveedA: A { new void DoSomething(); int AInt { get; } } public class Fuck : DeriveedA { public int AInt { get; private set;//固然了这里也能够是protected
 } public void DoSomething() { } }
 
实现C#显式接口:
       上面的实现都属于C#的接口的隐式实现,那显式实现是什么东西?看下面的例子:
public class Starter { /// <summary>
    /// 程序入口点 /// </summary>
    /// <param name="args"></param>
    public static void Main(string[] args) { Fuck test = new Fuck(); } } public interface IFuck { void Haha(); } public class Fuck :IFuck { void IFuck.Haha() { } }
 
  这个时候若是咱们直接使用test对象,是没法调用Haha这个方法的,由于若是一个类显示实现了一个接口,那么这个接口函数将是private的,外部没法直接调用这个函数,除非把类显式转换为接口:
public class Starter { /// <summary>
    /// 程序入口点 /// </summary>
    /// <param name="args"></param>
    public static void Main(string[] args) { Fuck test = new Fuck(); IFuck interfaceFuck = (IFuck)test; interfaceFuck.Haha();//这个时候至关于可使用test.Haha这个方法了
 } } public interface IFuck { void Haha(); } public class Fuck :IFuck { void IFuck.Haha()//注意显式实现接口不能带访问修饰符
 { } }
  
  可能有人问为何不把接口方法的实现定义为private,这个在C#里面是不容许的,若是一个类实现了一个接口(隐式实现),那么这个接口不能是private或者是protected的,必须是public的(必须是公共接口),这其实理解起来很天然,由于每每咱们把一个类继承一个接口,就是须要这个接口所声明的方法。
  那么为何还须要显式实现呢?从上面的例子咱们能够看到,若是显式实现了一个接口,那么直接经过类来访问接口的方法是不行的,必须是显式转换为接口才能够访问,这就至关于了一个private的功能,好比我继承了一个IList<T>,我可能只须要IList<T>接口的一部分,那么我能够把我须要的部分隐式实现,不须要的部分显式实现,那么这个时候我既能够隐藏我不须要用到的方法,也能够把它当成一个IList<T>来用(用的时候转换为接口就行了)。这在软件工程里面是很常见的,有些时候咱们写了一个类让框架来绑定,可是咱们不想有些接口被误用,可是又想咱们能够把它当作实现了这个接口的类型来用的时候,这样的作法就是最好的。很是符合面向对象的思想。
 
  再举几个能够用显示接口的例子。
  好比如今我有一个航空公司,公司里面有不少航班,可是其中B航班和C航班是特价航班,换句话说,就是航班之间的价格订价是不同的,咱们先假定一下全部航班的差异就是价格。
  那么咱们很容易想到咱们能够实现一个这样的航班类,可是咱们若是要查询价格的时候,当咱们显示查询B,C航班价格时,使用其各自特殊的计算方法,其余航班则选择统一的方法,在C#里面咱们能够这样实现:
public class Starter
{
    /// <summary>
    /// 程序入口点
    /// </summary>
    /// <param name="args"></param>
    public static void Main(string[] args)
    {
        Flys fly = new Flys();
        IFlyB flyB = fly;
        flyB.Cost();//计算航班B的价格

        IFlyC flyC = fly;
        flyC.Cost();//计算航班C的价格

        fly.Cost();//计算普通航班的价格

        Console.ReadKey();
    }
}
public interface IFlyB { void Cost(); } public interface IFlyC { void Cost(); } public class Flys :IFlyB,IFlyC { public void Cost() { Console.WriteLine("Other fly"); } void IFlyB.Cost() { Console.WriteLine("Fly B"); } void IFlyC.Cost() { Console.WriteLine("Fly C"); } }

  
  固然了,若是看过Effective C++的人已经知道我说的就是这本书上的例子,那么在C++的实现方法能够是实现一个Flys的虚基类,把Cost设为虚函数,而后派生出FlyC和FlyB,重写Cost,其余航班就显式使用虚函数的默认方法:
class Flys { public: virtual void cost()const = 0 { std::cout << "Other fly" << std::endl; } }; class FlyB :public Flys { public: void cost()const override { std::cout << "FlyB" << std::endl; } }; class FlyC :public Flys { public: void cost()const override { std::cout << "FlyC" << std::endl; } }; class OtherFly :public Flys { public: void cost()const override { Flys::cost(); } };
  
  这是一个C++的Best practice,由于这样写之后咱们每次定义一种Fly都必须提供Fly的定义,可使用默认定义,减小了程序员犯错的可能, C++能够这样写是由于C++的纯虚函数是一个很奇葩的东西,自己它应该是相似于C#的interface才对,可是却有了默认行为。
 
  第二个例子是,当我有两个接口,可是在一个在一个接口里面声明了名为Item的属性,另外一个接口声明了Item的一个方法,若是我一个类要同时继承这两个接口,怎么办呢?
public interface IOne { int Item { get; set; } } public interface ITwo { int Item(); } public class Hey : IOne, ITwo { public int Item { get; set;} public int Item() { throw new NotImplementedException(); } }
  
  当你写出上面的代码的时候,编译器会报错,Item具备二义性,那么这个时候你必须显式实现一个接口:
public interface IOne { int Item { get; set; } } public interface ITwo { int Item(); } public class Hey : IOne, ITwo { public int Item { get; set;} int ITwo.Item() { } }
  
  第三种状况就是刚才说的,当有些接口你不想让别人使用,可是你却想定义本身的版本的时候,好比你想实现一个List,而且想得到一个当Remove的时候还能够获取到节点的一个方法,可是IList<T>接口并无声明这个方法,因而你能够这样写:
public class ListNode<T> : IList<T> { public T RemoveAt(int index) { } void IList<T>.RemoveAt(int index) { } }
  
  这样就实现了咱们的想法,并且接口还不容易被误用。甚至你还能够在显式实现的接口void IList<T>.RemoveAt(int index)里面抛出异常,说明不支持这种方法。
相关文章
相关标签/搜索