关于Interface 和 abstrac 的理解

来源:互联网 发布:手机lol视频软件 编辑:程序博客网 时间:2024/06/14 00:15

我相信有许多同行对于interface和abstract的理解都各不一样,我想说说我个人的理解:

先说最基本概念:

一:interface(接口)

      定义:接口只包含方法,属性,事件或索引器的签名。实现接口的类或者结构必须实现接口定义中指定的接口成员;

注意事项:

1. 不允许使用访问修饰符(public, private, protected,或者internal)修饰接口成员,所有的接口成员都是公共的。
2. 实现接口的类必须实现接口中的所有的方法
3. 接口不能定义字段成员,不能用关键字static,virtual,abstract或者sealed来定义接口成员
4. 不能实例化接口,但接口可以作为字段出现在其他类中,一个类可以实现多个接口,一个接口可以从一个或多个接口继承
5.实现接口的类可以显式实现该接口的成员。 显式实现的成员不能通过类实例访问,而只能通过接口实例访问。

举例说明:

        下面的例子演示了属性的声明,类中包含实现,实现类的任何实例都具有声明的属性:

interfaceIPoint
{
   // Property signatures:
   int x
   {
      get;
      set;
   }

   int y
   {
      get;
      set;
   }
}

classPoint :IPoint
{
   // Fields:
   privateint _x;
   privateint _y;

   // Constructor:
   publicPoint(int x,int y)
  
{
      _x = x;
      _y = y;
   }

   // Property implementation:
   publicint x
   {
      get
      {
         return _x;
      }

      set
      {
         _x = value;
      }
   }

   publicint y
   {
      get
      {
         return _y;
      }
      set
      {
         _y = value;
      }
   }
}
索引器的例子:
publicinterfaceISomeInterface
{
    //声明索引器
    intthis[int index]
    {
        get;
        set;
    }
}

// 实现接口
classIndexerClass :ISomeInterface
{
    privateint[] arr =newint[100];
    publicintthis[int index]
    {
        get
        {
            return arr[index];
        }
        set
        {
            arr[index] = value;
        }
    }
}

classMainClass
{
    staticvoidMain()
   
{
        IndexerClass test = new IndexerClass();
        System.Random rand = new System.Random();
       
        for (int i =0; i <10; i++)
        {
            test[i] = rand.Next();
        }
        for (int i =0; i <10; i++)
        {
            System.Console.WriteLine("Element #{0} = {1}", i, test[i]);
        }

     
        System.Console.WriteLine("Press any key to exit.");
        System.Console.ReadKey();
    }
}

方法和事件的例子就不再写了,大家肯定用的挺多的...

什么是显示接口实现呢?请看这个例子:

如果一个类实现的两个接口包含签名相同的成员,则在该类上实现此成员会导致这两个接口将此成员用作其实现。 如下示例中,所有对 Paint 的调用皆调用同一方法。

classTest
{
   
staticvoidMain()
   
{
        SampleClass sc =
new SampleClass();
        IControl ctrl = (IControl)sc;
        ISurface srfc = (ISurface)sc;

       
// 调用同一方法
        sc.Paint();
        ctrl.Paint();
        srfc.Paint();
    }
}


interfaceIControl
{
   
voidPaint();
}
interfaceISurface
{
   
voidPaint();
}
classSampleClass : IControl,ISurface
{
   
// ISurface.Paint and IControl.Paint同时调用
   
publicvoidPaint()
   
{
        Console.WriteLine(
"Paint method in SampleClass");
    }
}

// Output:
// Paint method in SampleClass
// Paint method in SampleClass
// Paint method in SampleClass

但是,如果两个接口成员不执行同一功能,则会导致其中一个接口或两个接口的实现不正确。 创建仅通过接口调用且特定于该接口的类成员,则有可能显式实现接口成员。 这可通过使用接口名称和句点命名类成员来完成。 例如:

publicclassSampleClass :IControl,ISurface
{
    void IControl.Paint()
    {
        System.Console.WriteLine("IControl.Paint");
    }
    void ISurface.Paint()
    {
        System.Console.WriteLine("ISurface.Paint");
    }
}

类成员 IControl.Paint 仅通过 IControl 接口可用,ISurface.Paint 仅通过 ISurface 可用。 这两个方法实现相互独立,两者均不可直接在类上使用。 例如:

SampleClass obj = new SampleClass();
//obj.Paint();  // 会编译错误

IControl c = (IControl)obj;
c.Paint();  // 用对应的接口调用

ISurface s = (ISurface)obj;
s.Paint(); // Calls ISurface.Paint on SampleClass.

// Output:
// IControl.Paint
// ISurface.Paint

显式实现还用于处理两个接口分别声明名称相同的不同成员(例如属性和方法)的情况:

C#
interface ILeft{    int P { get;}}interface IRight{    int P();}

若要实现两个接口,类必须对属性 P 或方法 P 使用显式实现,或对二者同时使用,从而避免编译器错误。 例如:

C#
class Middle : ILeft, IRight{    public int P() { return 0; }    int ILeft.P { get { return 0; } }}

二:抽象类abstract

      基本概念:通过在类定义前面放置关键字 abstract,可以将类声明为抽象类

    注意事项:

                   1:抽象类不能实例化。 抽象类的用途是提供一个可供多个派生类共享的通用基类定义。

                   2:抽象类也可以定义抽象方法。 方法是将关键字 abstract 添加到方法的返回类型的前面。抽象方法没有实现,所以方法定义后面是分号,而不是常规的方法块。

                   3: 抽象类的派生类必须实现所有抽象方法。 当抽象类从基类继承虚方法时,抽象类可以使用抽象方法重写该虚方法。

                   4:抽象类是类,所以只能被单继承

                   5:无法使用 sealed 修饰符来修改抽象类,因为两个修饰符具有相反的含义。 sealed 修饰符阻止类被继承,而 abstract 修饰符要求类被继承

      6:在静态属性上使用 abstract 修饰符是错误的。

    

     抽象方法具有以下功能:

  • 抽象方法是隐式的虚拟方法。

  • 只有抽象类中才允许抽象方法声明。

  • 由于抽象方法声明不提供实际的实现,因此没有方法主体;方法声明仅以分号结尾,且签名后没有大括号 ,实现由替代方法 override 提供,它是非抽象类的成员在抽象方法声明中使用 static 或 virtual 修饰符是错误的。

  • 通过包含使用 override 修饰符的属性声明,可在派生类中重写抽象继承属性。

     举例说明:

public class D{    public virtual void DoWork(int i)    {    }}public abstract class E : D{    public abstract override void DoWork(int i);}public class F : E{    public override void DoWork(int i)    {        // 新复写内容    }}

如果将 virtual 方法声明为 abstract,则该方法对于从抽象类继承的所有类而言仍然是虚方法。 继承抽象方法的类无法访问方法的原始实现,因此在上一示例中,类 F 上的 DoWork 无法调用类 D 上的 DoWork。通过这种方式,抽象类可强制派生类向虚拟方法提供新的方法实现。



举例说明interface和abstract理解上的区别,实质是设计思想上有着不同,比如:有Icar接口和Acar抽象类,车本身就有能开的基本功能,通过接口或者抽象类都可以做到,

internal interface Icar
{
    void Driver();
}


public  abstract class ACar
{
   public  abstract void Driver();
}


public class QQ : Icar
{
    public void Driver()
    {
        // ...
    }
}


public class QQ1 : ACar
{
    public override void Driver()
    {
        // ...
    }
}

但是过了一段时间,我想给车加个自动泊车功能(autohold),你可能会想到这样做:

internal interface Icar
{
    void Driver();
    void AutoHold();
}


public  abstract class ACar
{
   public  abstract void Driver();
   public abstract void AutoHold();
}

接口或者抽象类都加上这个功能不就好了么,认真想想,不是所有的车都会具有这个功能的,只有高端一点的车才会有,所有车本身肯定能开,但不一定会有autohold功能。QQ 是一个Car,继承Acar(是一个car),具有autohold功能,额外添加的功能

我们把autohold功能抽象为一个额外功能接口,让高档的车实现这个接口就可以啦

interface IOtherFaction
{
    void AutoHold();
}

public class BMW : ACar, IOtherFaction
{
    public override void Driver()
    {
        // ...车都具有的功能
    }

    public void AutoHold()
    {
        //高档车额外的新功能
    }
}

public class QQ1 : ACar
{
    public override void Driver()
    {
        // ...车都具有的功能
    }
}

我的理解就写到这里,有什么不妥之处,望交流。


原创粉丝点击