詳解C#中抽象類別與介面的區別

黄舟
發布: 2017-03-23 11:20:53
原創
1529 人瀏覽過

本文主要介紹了C#中抽象類別與介面的差異。具有很好的參考價值。下面跟著小編一起來看下吧

1.面向介面程式設計和物件導向程式設計是什麼關係

首先,面向介面編程和物件導向程式設計並不是平級的,它並不是比物件導向程式設計更先進的一種獨立的程式設計思想,而是附屬於物件導向思想體系,屬於其一部分。或者說,它是物件導向程式設計體系中的思想精髓之一。

2.接口的本質

接口,在表面上是由幾個沒有主體代碼的方法定義組成的集合體,有唯一的名稱,可以被類別或其他介面所實作(或者也可以說繼承)。它在形式上可能是如下的樣子:

interface InterfaceName
{
 void Method1();
 void Method2(int para1);
 void Method3(string para2,string para3);
}
登入後複製

那麼,介面的本質是什麼呢?或者說介面存在的意義是什麼。我認為可以從以下兩個視角考慮:

1)介面是一組規則的集合,它規定了實現本介面的類別或介面必須擁有的一組規則。體現了自然界「如果你是…就必須能…」的理念。

例如,在自然界中,人都能吃飯,即「如果你是人,就必須能吃飯」。那麼模擬到電腦程式中,就應該有一個IPerson(習慣上,介面名稱由「I」開頭)接口,並有一個方法叫Eat(),然後我們規定,每一個表示「人」的類,必須實現IPerson接口,這就模擬了自然界「如果你是人,就必須能吃」這條規則。

從這裡,我想各位也能看到些許物件導向思想的東西。物件導向思想的核心之一,就是模擬真實世界,把真實世界中的事物抽象成類,整個程式靠各個類別的實例互相通訊、互相協作完成系統功能,這非常符合真實世界的運作狀況,也是面向對象思想的精髓。

2)介面是在某一粒度視圖上同類事物的抽象表示。注意這裡我強調了在某一粒度視圖上,因為「同類事物」這個概念是相對的,它因為粒度視圖不同而不同。

例如,在我的眼裡,我是一個人,和一頭豬有本質區別,我可以接受我和我同學是同類這個說法,但絕不能接受我和一頭豬是同類。但是,如果在一個動物學家眼裡,我和豬應該是同類,因為我們都是動物,他可以認為“人”和“豬”都實現了IAnimal這個接口,而他在研究動物行為時,不會把我和豬分開對待,而會從「動物」這個較大的粒度上研究,但他會認為我和一棵樹有本質區別。

現在換了一個遺傳學家,情況又不同了,因為生物都能遺傳,所以在他眼裡,我不僅和豬沒區別,和一隻蚊子、一個細菌、一顆樹、一個蘑菇甚至一個SARS病毒都沒什麼區別,因為他會認為我們都實現了IDescendable這個接口(註:descend vi. 遺傳),即我們都是可遺傳的東西,他不會分別研究我們,而會將所有生物作為同類進行研究,在他眼裡沒有人和病毒之分,只有可遺傳的物質和不可遺傳的物質。但至少,我和一塊石頭還是有差別的。

可不幸的事情發生了,某日,地球上出現了一位偉大的人,他叫列寧,他在熟讀馬克思、恩格斯的辯證唯物主義思想巨著後,頗有心得,於是他下了一個著名的定義:所謂物質,就是能被意識所反映的客觀實在。至此,我和一塊石頭、一絲空氣、一條成語和傳輸手機訊號的電磁場已經沒什麼區別了,因為在列寧的眼裡,我們都是可以被意識所反映的客觀實在。如果列寧是程式設計師,他會這麼說:所謂物質,就是所有同時實作了「IReflectabe」和「IEsse」兩個介面的類別所產生的實例。 (註:reflect v. 反映  esse n. 客觀實在)

也許你會覺得我上面的例子像在瞎掰,但是,這正是介面得以存在的意義。物件導向思想和核心之一叫做多態性,什麼叫多態性?說穿了就是在某個粒度視圖層面上對同類事物不加區別的對待而統一處理。而之所以敢這樣做,就是因為有介面的存在。像那個遺傳學家,他明白所有生物都實現了IDescendable接口,那隻要是生物,一定有Descend()這個方法,於是他就可以統一研究,而不至於分別研究每一種生物而最終累死。

可能這裡還不能給你一個關於介面本質和作用的直覺印象。那麼在後文的例子和對幾個設計模式的解析中,你將會更直覺地體驗到介面的內涵。

3.面向介面程式設計綜述

那麼什麼是面向介面程式設計呢?我個人的定義是:在系統分析和架構中,分清層次和依賴關係,每個層次不是直接向其上層提供服務(即不是直接實例化在上層中),而是透過定義一組接口,僅向上層暴露其接口功能,上層對於下層僅是接口依賴,而不依賴具體類。

這樣做的好處是顯而易見的,首先對系統彈性大有好處。當下層需要改變時,只要介面及介面功能不變,則上層不用做任何修改。甚至可以在不改動上層程式碼時將下層整個替換掉,就像我們將一個WD的60G硬碟換成一個希捷的160G的硬碟,電腦其他地方不用做任何改動,而是把原硬碟拔下來、新硬碟插上就行了,因為電腦其他部分不依賴具體硬碟,而只依賴一個IDE接口,只要硬碟實現了這個接口,就可以替換上去。從這裡看,程式中的介面和現實中的介面極為相似,所以我一直認為,介面(interface)這個字用的真是神似!

使用介面的另一個好處就是不同部件或層次的開發人員可以並行開工,就像造硬碟的不用等造CPU的,也不用等造顯示器的,只要介面一致,設計合理,完全可以並行進行開發,從而提高效率。

對本文的補充:

#1.關於「面向介面程式設計」中的「介面」與具體物件導向語言中「介面」兩個字

看到有朋友提出「面向介面程式設計」中的「介面」二字應該比單純程式語言中的interface範圍更大。我經過思考,覺得很有道理。這裡我寫的確實不太合理。我想,物件導向語言中的「介面」是指具體的一種程式碼結構,例如C#中用interface關鍵字定義的介面。而「面向介面程式設計」中的「介面」可以說是一種從軟體架構的角度、從一個更抽象的層面上指那種用來隱藏特定底層類別和實現多態性的結構部件。從這個意義上說,如果定義一個抽象類,並且目的是為了實現多態,那麼我認為把這個抽象類別也稱為「介面」是合理的。但是用抽象類別實現多態合理不合理?在下面第二條討論。

概括來說,我覺得兩個「介面」的概念既相互區別又相互連結。 「面向介面程式設計」中的介面是一種思想層面的用於實現多態性、提高軟體靈活性和可維護性的架構部件,而具體語言中的「介面」是將這種思想中的部件具體實施到程式碼裡的手段。

2.關於抽象類別與介面

如果單從具體程式碼來看,對這兩個概念很容易模糊,甚至覺得介面就是多餘的,因為單從具體功能來看,除多重繼承外(C#,Java中),抽象類別似乎完全能取代介面。但是,難道介面的存在是為了實現多重繼承?當然不是。我認為,抽象類別和介面的區別在於使用動機。使用抽象類別是為了程式碼的複用,而使用介面的動機是為了實現多態性。所以,如果你在為某個地方該使用介面還是抽象類別而猶豫不決時,那麼可以想想你的動機是什麼。

看到有朋友對IPerson這個介面的質疑,我個人的理解是,IPerson這個介面該不該定義,關鍵看具體應用中是怎麼個情況。如果我們的專案中有Women和Man,都繼承Person,而且Women和Man絕大多數方法都相同,只有一個方法DoSomethingInWC()不同(例子比較粗俗,各位見諒),那麼當然定義一個AbstractPerson抽象類別比較合理,因為它可以把其他所有方法都包含進去,子類別只定義DoSomethingInWC(),大大減少了重複程式碼量。

但是,如果我们程序中的Women和Man两个类基本没有共同代码,而且有一个PersonHandle类需要实例化他们,并且不希望知道他们是男是女,而只需把他们当作人看待,并实现多态,那么定义成接口就有必要了。

总而言之,接口与抽象类的区别主要在于使用的动机,而不在于其本身。而一个东西该定义成抽象类还是接口,要根据具体环境的上下文决定。

再者,我认为接口和抽象类的另一个区别在于,抽象类和它的子类之间应该是一般和特殊的关系,而接口仅仅是它的子类应该实现的一组规则。(当然,有时也可能存在一般与特殊的关系,但我们使用接口的目的不在这里)如,交通工具定义成抽象类,汽车、飞机、轮船定义成子类,是可以接受的,因为汽车、飞机、轮船都是一种特殊的交通工具。再譬如Icomparable接口,它只是说,实现这个接口的类必须要可以进行比较,这是一条规则。如果Car这个类实现了Icomparable,只是说,我们的Car中有一个方法可以对两个Car的实例进行比较,可能是比哪辆车更贵,也可能比哪辆车更大,这都无所谓,但我们不能说“汽车是一种特殊的可以比较”,这在文法上都不通。

C#.NET里面抽象类和接口有什么区别?

接口和抽象类的概念不一样。接口是对动作的抽象,抽象类是对根源的抽象。

抽象类表示的是,这个对象是什么。接口表示的是,这个对象能做什么。比如,男人,女人,这两个类(如果是类的话……),他们的抽象类是人。说明,他们都是人。

人可以吃东西,狗也可以吃东西,你可以把“吃东西”定义成一个接口,然后让这些类去实现它.

所以,在高级语言上,一个类只能继承一个类(抽象类)(正如人不可能同时是生物和非生物),但是可以实现多个接口(吃饭接口、走路接口)。

下面接着再说说两者在应用上的区别:

接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约。

而抽象类在代码实现方面发挥作用,可以实现代码的重用

模板方法设计模式是抽象类的一个典型应用

最佳答案:

1抽象类

(1) 抽象方法只作声明,而不包含实现,可以看成是没有实现体的虚方法

(2) 抽象类不能被实例化

(3) 抽象类可以但不是必须有抽象属性和抽象方法,但是一旦有了抽象方法,就一定要把这个类声明为抽象类

(4) 具体派生类必须覆盖基类的抽象方法

(5) 抽象派生类可以覆盖基类的抽象方法,也可以不覆盖。如果不覆盖,则其具体派生类必须覆盖它们。如:

using System;
public abstract class A //抽象类A 
{ 
 private int num=0;
 public int Num //抽象类包含属性 
 { 
  get 
  { 
   return num; 
  } 
  set 
  { 
   num = value; 
  }   
 }
 public virtual int getNum() //抽象类包含虚方法 
 { 
  return num; 
 }
 public void setNum(int n) // //抽象类包含普通方法 
 { 
  this.num = n; 
 }
 public abstract void E(); //类A中的抽象方法E  
}
public abstract class B : A //由于类B继承了类A中的抽象方法E,所以类B也变成了抽象类 
{
}
public class C : B 
{ 
 public override void E() //重写从类A继承的抽象方法。如果类B自己还定义了抽象方法,也必须重写 
 { 
  //throw new Exception("The method or operation is not implemented."); 
 } 
}
public class Test 
{ 
 static void Main() 
 { 
  C c = new C(); 
  c.E(); 
 } 
}
登入後複製

二、接 口

(1) 接口不能被实例化

(2) 接口只能包含方法声明

(3) 接口的成员包括方法、属性、索引器、事件

(4) 接口中不能包含常量、字段(域)、构造函数、析构函数、静态成员。如:

public delegate void EventHandler(object sender, Event e);
public interface ITest 
{ 
 //int x = 0;
 int A 
 { 
  get; 
  set; 
 }
 void Test();
 event EventHandler Event; 
 int this[int index] 
 { 
  get;
  set; 
 } 
}
登入後複製

(5) 接口中的所有成员默认为public,因此接口中不能有private修饰符

(6) 派生类必须实现接口的所有成员

(7) 一个类可以直接实现多个接口,接口之间用逗号隔开

(8) 一个接口可以有多个父接口,实现该接口的类必须实现所有父接口中的所有成员

三、抽象类和接口

相同点:

(1) 都可以被继承

(2) 都不能被实例化

(3) 都可以包含方法声明

(4) 派生类必须实现未实现的方法

区 别:

(1) 抽象基类可以定义字段、属性、方法实现。接口只能定义属性、索引器、事件、和方法声明,不能包含字段。

(2) 抽象类是一个不完整的类,需要进一步细化,而接口是一个行为规范。微软的自定义接口总是后带able字段,证明其是表述一类“我能做。。。”

(3) 接口可以被多重实现,抽象类只能被单一继承

(4) 抽象类更多的是定义在一系列紧密相关的类间,而接口大多数是关系疏松但都实现某一功能的类中

(5) 抽象類別是從一系列相關物件中抽像出來的概念, 因此反映的是事物的內部共通點;介面是為了滿足外部呼叫而定義的一個功能約定, 因此反映的是事物的外部特性

(6) 介面基本上不具備繼承的任何特定特點,它僅僅承諾了能夠呼叫的方法   

(7) 介面可以用來支援回呼,而繼承並不具備這個特點

(8) 抽象類別實作的具體方法預設為虛的,但實作介面的類別中的介面方法卻預設為非虛的,當然您也可以宣告為虛的

(9) 如果抽象類別實作接口,則可以把接口中方法映射到抽象類別中作為抽象方法而不必實現,而在抽象類別的子類別中實作介面中方法

以上是詳解C#中抽象類別與介面的區別的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板