.NET Core CSharp初級篇 1-5 接口、枚舉、抽象


.NET Core CSharp初級篇 1-5

本節內容類的接口、枚舉、抽象

簡介

問題

  • 如果你需要表示星期或者是某些狀態,使用字符串或者數字是否不直觀?
  • 你是否發現,無論何種電腦,它的USB口的設計都是遵循一定規范的?

枚舉

枚舉(enum)是一個非常好用的一個特殊值類型,他可以讓你指定一系列字符常量(通常從0開始)。它的定義和使用如下:

public enum Week
{
    Monday,
    ...//此處省略
    Sunday = 6//可賦值
}
bool flag = (6 == (int)Week.Sunday)

不過你也可以指定其他的類型作為枚舉的值,例如:

public enum Week:byte
{
    Monday,
    ...
    Sunday= 6
}

枚舉與其他類型的轉換使用強制類型轉換即可,例如:(int)Week.Sunday,不過特別的,0不需要強制轉換就可以和枚舉進行比較。

Flag Enum

這是一個有趣的枚舉,它支持你對枚舉按位進行運算,使用flag enum需要在枚舉名上面指定一個Attribute,也就是[Flags],通常來說,我們會使用2的冪作為枚舉值,因為按位運算本質是2進制的運算。

具體實例如下

[Flags]
public enum Status
{
    Success = 1,
    NotFound = 2,
    Fail = 4
}
//支持按位運算,運算步驟我們在之前已經有過詳細的講解
Status.Success | Status.NotFound

默認的,如果你輸入了一個不合理的枚舉值(也就是沒定義),編譯器會默認直接輸出該數字,不過如果你使用了按位運算的枚舉,那么他會將你輸入的數字轉換成二進制與每一個枚舉值進行&運算,得出的結果會與枚舉值進行比較,如果找到了就會輸出。

例如以上例的Status:

Console.WriteLine((Status)7);
//輸出是三個都輸出
//7 = 0111,
//1 = 0001,
//2 = 0010
//4 = 0011

接口

接口這個東西,新手非常容易被誤導,例如在WebApi開發中,你的前端朋友讓你把接口給他,這個時候,他需要的東西在后端的口中叫做API,
當你的后端朋友說,你寫一個接口,我們使用依賴注入進行統一管理實現了接口的類,這個時候,他需要的是一個約定,也是我們這里講的接口(interface)。

接口是C#面向對象中實現多態的重要語法。接口的定義可以理解為是一種約定的規范。例如電腦的USB-A接口,全世界的廠商都是統一規范生產,如果大家不按着約定生產,后果會是什么?

在C#中,接口也一樣起到了這個作用,但是還有一些更為廣泛的應用。

接口的定義使用interface關鍵字,默認的,所有接口的成員的訪問權限都是public,因為規范是需要公布給所有人看,如果定義了訪問權限就沒有實際的意義了
,並且接口中所有的函數都不存在函數體。總的來說就是,接口是一個提供類的規格的東西,卻不提供實現。

接口的例子

//定義一個接口
public inteface IHuman
{
    void Eat();
    bool Alive();
}
//接口的實現,必須實現每一個接口中的函數並保持返回類型、函數簽名,函數參數一致
public class Human:IHuman
{
    void Eat()
    {

    }
    bool Alive()
    {
        return default<bool>();
    }
}

接口的實現會和我們后面講到的繼承非常相似,在這里,你只需要記住接口支持一個類實現多個接口,但只能繼承一個類即可。

接口沖突

因為支持一個類實現多個接口,那么很有可能會造成A接口中擁有和B接口中完全一致的函數,這個時候我們就需要使用顯式實現接口進行處理。

當你需要調用不同接口的同簽名方法時,使用接口進行強制轉換即可。

例如:

interface IApple
{
    void Wash();
}
interface IFruit
{
    void Wash();
}
class test : IApple,IFruit
{
    void IApple.Wash()=>{};
    void IFruit.Wash()=>{};
}
test t= new test();
((IApple)t).Wash();
((IFruit)t).Wash();

這樣就可以避免接口在命名上的沖突。

並且接口如果你隱式的實現,所有接口函數默認都是sealed的。
如果存在一個多繼承的問題,這個可能目前講起來為之過早,我就順口提一下,例如,人類繼承與動物類,動物繼承於生物接口,
那么對於人類,是隱式的繼承了生物接口,但是對於人類和動物,進食的方式有很大區別,那么我們就應該重寫進食這個方法。
我們就要把基類(父類)中的接口函數標記為virtual或者abstract,然后在子類中使用override進行重寫。

這就已經說的太遠了,后面我們會進行深入的刨析。

抽象

抽象可以有抽象字段、抽象類、抽象委托、抽象函數等等。我們就以其中常用的抽象函數和抽象類做一個解析。

抽象和接口非常相似,抽象類不能被實例化,抽象方法沒有方法體,都是依賴子類(被繼承類)進行操作。

抽象函數

這個就和接口幾乎一模一樣,也沒有太多講的必要,如果你聲明了一個函數是抽象函數,那么它不存在方法體,你需要通過子類去重寫(override)實現。

在實際應用中,子類僅能重寫父類中的虛方法或者抽象方法,當不需要使用父類中方法的內容時,將其定義成抽象方法,否則將方法定義成虛方法。

抽象類

"一個包含一個或多個純虛函數的類叫抽象類,抽象類不能被實例化,進一步
一個抽象類只能通過接口和作為其它類的基類使用."

一個抽象類可以包含抽象和非抽象方法,當一個類繼承於抽象類,那么這個派生類必須實現所有的
的基類抽象方法。

但是通過聲明派生類也為抽象,我們可以避免所有或特定的虛方法的實現,
這就是抽象類的部分實現。

看起來很高深?事實上抽象類就是一個提供了有部分沒有方法體的函數和有具體實現的函數的集合。它相比於接口毫無實現而言,抽象類可以提供非抽象的方法,也就是說,抽象類中可以含有有實現方法的函數。

看這個例子

public abstract class A
{
    public void GetSomeThing()
    {
        //todo
    }
    public abstract void SetSomeThing();
}
public class B:A
{
    //實現抽象方法
    public override void SetSomeThing()
    {
        //調用非抽象方法
        base.GetSomeThing();
    }

}

這里面涉及到了base關鍵字以及":"繼承符號,在后面的繼承、多態的課程有會有更加深入的介紹。

Github

BiliBili主頁

WarrenRyan's Blog

博客園


免責聲明!

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



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