.NET Core CSharp 初級篇 1 5 本節內容類的介面、枚舉、抽象 簡介 問題 如果你需要表示星期或者是某些狀態,使用字元串或者數字是否不直觀? 你是否發現,無論何種電腦,它的USB口的設計都是遵循一定規範的? 枚舉 枚舉(enum)是一個非常好用的一個特殊值類型,他可以讓你指定一系列字 ...
.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關鍵字以及":"繼承符號,在後面的繼承、多態的課程有會有更加深入的介紹。