c#中抽象類和接口的相同點跟區別


下面是自己寫的一個demo,體現抽象類和接口的用法。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace 抽象類和接口
{
    /*
     *    相同點:
    (1) 都可以被繼承
    (2) 都不能被實例化
    (3) 都可以包含方法聲明
    (4) 派生類必須實現未實現的方法
    區 別:
    (1) 抽象基類可以定義字段、屬性、方法實現。接口只能定義屬性、索引器、事件、和方法聲明,不能包含字段。
    (2) 抽象類是一個不完整的類,需要進一步細化,而接口是一個行為規范。微軟的自定義接口總是后帶able字段,證明其是表述一類“我能做。。。”
    (3) 接口可以被多重實現,抽象類只能被單一繼承
    (4) 抽象類更多的是定義在一系列緊密相關的類間,而接口大多數是關系疏松但都實現某一功能的類中
    (5) 抽象類是從一系列相關對象中抽象出來的概念, 因此反映的是事物的內部共性;接口是為了滿足外部調用而定義的一個功能約定, 因此反映的是事物的外部特性
    (6) 接口基本上不具備繼承的任何具體特點,它僅僅承諾了能夠調用的方法    
    (7) 接口可以用於支持回調,而繼承並不具備這個特點
    (8) 抽象類實現的具體方法默認為虛的,但實現接口的類中的接口方法卻默認為非虛的,當然您也可以聲明為虛的 
    (9) 如果抽象類實現接口,則可以把接口中方法映射到抽象類中作為抽象方法而不必實現,而在抽象類的子類中實現接口中方法
     */
    public delegate void EnableHandler(object sender, EventArgs e);
    public delegate void CPU_CallBack(CPU cpu);
 
    public interface i7_CPU_Interface
    {
        void LGA2011();
        bool Enable
        {
            get;
        }
        int this[int index]
        {
            get;
        }
 
        event EventHandler EnableLGA2011;
    }
 
    public abstract class CPU : i7_CPU_Interface
    {
        public event EventHandler EnableLGA2011;
        int[] pins;
        string _name;
        double _frequency;
        int _kernel;
        int _pinNum;
        bool _isEnableLGA2011;
        public CPU(string name, double frequency, int kernel, int pin, bool isEnableLGA2011)
        {
            _name = name;
            _frequency = frequency;
            _kernel = kernel;
            _pinNum = pin;
            pins=new int[pin];
            _isEnableLGA2011 = isEnableLGA2011;
            EnableLGA2011 += new EventHandler(CPU_EnableLGA2011);
        }
 
        void CPU_EnableLGA2011(object sender, EventArgs e)
        {
            Console.WriteLine("我支持LGA2011");
        }
 
        public string Name
        {
            get
            {
                return _name;
            }
        }
        public double Frequency
        {
            get
            {
                return _frequency;
            }
        }
        public int Kernel
        {
            get
            {
                return _kernel;
            }
        }
        public int PinNum
        {
            get
            {
                return _pinNum;
            }
        }
 
        protected abstract void PowerOn();
        protected abstract void SelfCheck();
        public override string ToString()
        {
            return string.Format("我是{0} CPU,我的主頻是{1:0.0},我有{2}個內核,我的引腳有{3}個。", _name, _frequency, _kernel, _pinNum);
        }
 
        public void LGA2011()
        {
            Console.WriteLine("1、處理器最高可達八核。");
            Console.WriteLine("2、支持四通道DDR3內存。");
            Console.WriteLine("3、支持PCI-E 3.0規范。");
            Console.WriteLine("4、芯片組使用單芯片設計,支持兩個SATA 3Gbps和多達十個SATA/SAS 6Gbps接口。");
        }
        public bool Enable
        {
            get
            {
                if (_isEnableLGA2011)
                {
                    EnableLGA2011.Invoke(this, null);
                }
                return _isEnableLGA2011;
            }
        }
        public int this[int index]
        {
            get
            {
                if (index < pins.Length)
                    return pins[index];
                return -1;
            }
        }
 
 
    }
 
    public class i7_4770k : CPU
    {
        public i7_4770k()
            : base("i7-4770k", 3.6, 8, 2011,true)
        {
            Console.WriteLine(base.ToString());
            PowerOn();
            SelfCheck();
        }
        protected override void PowerOn()
        {
            Console.WriteLine("i7-4770k 上電完成");
        }
        protected override void SelfCheck()
        {
            Console.WriteLine("i7-4770k 自檢完成");
        }
    }
 
    public class i7_4790k : CPU
    {
        public i7_4790k()
            : base("i7-4790k", 4.0, 8, 2011, true)
        {
            Console.WriteLine(base.ToString());
            PowerOn();
            SelfCheck();
        }
        protected override void PowerOn()
        {
            Console.WriteLine("i7-4790k 上電完成");
        }
        protected override void SelfCheck()
        {
            Console.WriteLine("i7-4790k 自檢完成");
        }
    }
 
    public class CreatCPU
    {
        CPU_CallBack callback;
        CPU _cpu = null;
       
        public CreatCPU(CPU cpu)
        {
            _cpu = cpu;
            //接口回調,任何實現了該接口的對象都可以被CreatCPU類對象所回調,保證了代碼對環境的適應性
            callback = new CPU_CallBack(cpu_call_back);
        }
       
        void cpu_call_back(CPU u)
        {
            if (u.Enable)
                u.LGA2011();
        }
        public void ShowMessage()
        {
            callback.Invoke(_cpu);
        }
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            CreatCPU creatI7_4770k = new CreatCPU(new i7_4770k());
            creatI7_4770k.ShowMessage();
 
            Console.WriteLine();
 
            CreatCPU creatI7_4790k = new CreatCPU(new i7_4790k());
            creatI7_4790k.ShowMessage();
   
            Console.Read();
 
        }
    }
}

 用了幾年C#,總結一下抽象類和接口的相同點與不同點。看了別人寫的感覺很全面:

相同點:
    (1) 都可以被繼承
    (2) 都不能被實例化
    (3) 都可以包含方法聲明
    (4) 派生類必須實現未實現的方法
區 別:
    (1) 抽象基類可以定義字段、屬性、方法實現。接口只能定義屬性、索引器、事件、和方法聲明,不能包含字段。
    (2) 抽象類是一個不完整的類,需要進一步細化,而接口是一個行為規范。微軟的自定義接口總是后帶able字段,證明其是表述一類“我能做。。。”
    (3) 接口可以被多重實現,抽象類只能被單一繼承
    (4) 抽象類更多的是定義在一系列緊密相關的類間,而接口大多數是關系疏松但都實現某一功能的類中
    (5) 抽象類是從一系列相關對象中抽象出來的概念, 因此反映的是事物的內部共性;接口是為了滿足外部調用而定義的一個功能約定, 因此反映的是事物的外部特性
   (6) 接口基本上不具備繼承的任何具體特點,它僅僅承諾了能夠調用的方法
    (7) 接口可以用於支持回調,而繼承並不具備這個特點
    (8) 抽象類實現的具體方法默認為虛的,但實現接口的類中的接口方法卻默認為非虛的,當然您也可以聲明為虛的
    (9) 如果抽象類實現接口,則可以把接口中方法映射到抽象類中作為抽象方法而不必實現,而在抽象類的子類中實現接口中方法

    使用規則:
    1、抽象類主要用於關系密切的對象,而接口最適合為不相關的類提供通用功能
    2、如果要設計大的功能單元,則使用抽象類;如果要設計小而簡練的功能塊,則使用接口。
    3、如果預計要創建組件的多個版本,則創建抽象類。接口一旦創建就不能更改。如果需要接口的新版本,必須創建一個全新的接口。
    4、如果創建的功能將在大范圍的全異對象間使用,則使用接口;如果要在組件的所有實現間提供通用的已實現功能,則使用抽象類。
    5、分析對象,提煉內部共性形成抽象類,用以表示對象本質,即“是什么”。為外部提供調用或功能需要擴充時優先使用接口
    6、好的接口定義應該是具有專一功能性的,而不是多功能的,否則造成接口污染。如果一個類只是實現了這個接口的中一個功能,而不得不去實現接口中的其他方法,就叫接口污染
    7、盡量避免使用繼承來實現組建功能,而是使用黑箱復用,即對象組合。因為繼承的層次增多,造成最直接的后果就是當你調用這個類群中某一類,就必須把他們全部加載到棧中!后果可想而知。(結合堆棧原理理解)。同時,有心的朋友可以留意到微軟在構建一個類時,很多時候用到了對象組合的方法。比如 asp.net中,Page類,有Server Request等屬性,但其實他們都是某個類的對象。使用Page類的這個對象來調用另外的類的方法和屬性,這個是非常基本的一個設計原則
    例 如:
    Window窗體可以用抽象類來設計,可以把公有操作和屬性放到一個抽象類里,讓窗體和對話框繼承自這個抽象類,再根據自己的需求進行擴展和完善。
    打印操作可以作為一個接口提供給每個需要此功能的窗體,因為窗體的內容不同,就要根據他們自己的要求去實現自己的打印功能。打印時只通過接口來調用,而不用在乎是那個窗體要打印。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM