溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

C#中面向?qū)ο蟮南嚓P(guān)知識點有哪些

發(fā)布時間:2023-02-27 10:28:16 來源:億速云 閱讀:97 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“C#中面向?qū)ο蟮南嚓P(guān)知識點有哪些”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!

switch和字典

前文提到過,有個游戲里面有個著名的屎山,就是跑了19億次if,把玩家憋得不行。而解決這個問題其實非常簡單,只需用到switch就可以了。

比如打牌的時候,正常只有2-10是數(shù)字,1是A,11是J,12是Q,13是K,如果要用if...else if這種方法來判斷,那么遇到K的時候需要判斷好多次才行,switch則只需一次

void cardName(int cardNum)
{
    switch (cardNum)
    {
        case 1: Console.WriteLine("A"); break;
        case 11: Console.WriteLine("J"); break;
        case 12: Console.WriteLine("Q"); break;
        case 13: Console.WriteLine("K"); break;
        default: Console.WriteLine(cardNum); break;
    }
}

C#中的switch語句,除了用break可以跳出switch之外,還可以用goto case xx來跳轉(zhuǎn)到第xx個case,這個特性還挺有意思的,所以在這里多提一嘴,但初學者其實只要有switch case這個概念就可以了。

switch case語句之所以在性能上優(yōu)于if...else,極有可能是用了哈希表,通過計算輸入的方法,來快速鏈接到執(zhí)行程序的入口,達到常數(shù)級別的時間復(fù)雜度。

在C#中,提供了字典這種數(shù)據(jù)結(jié)構(gòu),可以實現(xiàn)類似于switch case的效果。所謂字典,就是一組鍵值對,通過鍵值的一一對應(yīng)關(guān)系,達到通過鍵來索引值的目的,其定義方式如下

Dictionary<int, string> card = new Dictionary<int, string>
{
    {1,"A" },
    {11, "J" },
    {12, "Q" },
    {13, "K" }
};

Dictionary為數(shù)據(jù)類型的名字,<int, string>表示其鍵為整型,值為字符串。后面的new表示創(chuàng)建新對象,這個在數(shù)組的時候就已經(jīng)學過了,最后花括號中的四行代碼,用于對Dictionary進行初始化。

有了這個,就可以更簡潔地實現(xiàn)抽牌功能

Console.WriteLine(card[1]);
//命令行中顯示 A

那么這個時候可能有人問了,那2-10這9張牌咋辦?是需要加一個if來判斷嗎?

答案是當然不用,畢竟字典是一種動態(tài)的數(shù)據(jù)結(jié)構(gòu),內(nèi)部元素是可以增長的,只需跑個循環(huán)將其填充上就行了

for (int i = 2; i < 11; i++)
{
    card.Add(i, i.ToString());
}

其中,card.Add就是添加元素的方法,i.ToString()可以將整型的i轉(zhuǎn)化為字符串。

這樣,就有了從A到K的撲克牌。

類、成員、方法

撲克牌除了面值之外,還要看花色的。換言之,用數(shù)字是沒法完全描述撲克牌的所有屬性的。

當然,這種屬性其實可以用字典來實現(xiàn),例如現(xiàn)在有一個紅桃K,可以表示為

Dictionary<string, string> 紅桃K = new Dictionary<string, string>
{
    {"花色", "紅桃" },
    {"數(shù)值", "13" },
    {"名字", "K" }
};

首先,看到紅桃K千萬不要害怕,在C#中,中文也是可以當作變量名的。

其次,字典畢竟不太方便,因為C#中的字典要求指定數(shù)據(jù)類型,數(shù)值這個鍵對應(yīng)的值,按理說應(yīng)該是整型才比較合理,但無奈之下,只能是字符串。

面對這種痛點,就得看Class來大顯身手了。

class Card
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Color { get; set; }
}

其中,class表示,Card是一個類,后面的花括號里就是這個類的成員變量。

public表示,這是個公開的屬性,可以被別人調(diào)用;{get; set;}表示這個屬性既可以被調(diào)用,也可以被賦值。

需要注意的是,目前我們寫下的所有代碼,都是在.NET6中所定義的頂級語句。這種頂級語句,并不符合以往C#代碼的規(guī)范,由此也會導致一些問題,即頂級語句必須寫在所有類定義的前面。

所以,如果想創(chuàng)建一個Card類的實例,需要在class Card之前調(diào)用,這個很反直覺,但習慣了就好。

Card 紅桃A = new Card { Id = 1, Name = "A", Color = "紅桃" };

接下來遇到了一個很尷尬的問題,的確是新建了一個紅桃A,然后呢?

首先,可以通過.來調(diào)用類的屬性,例如

Console.WriteLine(紅桃A.Name);

其次,可以在類中添加成員方法,然后調(diào)用一下

class Card
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Color { get; set; }

    public void introduce()
    {
        Console.WriteLine($"我的名字是{Color}{Name}");
    }
}

調(diào)用仍然要在class Card這行的前面,

紅桃A.introduce();

得到的結(jié)果為

我的名字是紅桃A

是時候規(guī)范一下寫法了

頂級語句用起來雖然很爽,但,至少在目前看來,最適合的應(yīng)用場景是算法原理的快速驗證,而非做開發(fā)。因為開發(fā)要涉及到團隊合作,涉及到大家按照相同的規(guī)范去分塊做不同的內(nèi)容,為了團隊和整體的效率,不得不犧牲局部代碼的簡潔性,所以作為C#程序員,還是要習慣那種類似Java風格的完全的面向?qū)ο髮懛ā?/p>

重新建一個控制臺應(yīng)用,這次在選擇框架的界面,勾選上不使用頂級語句,這回看到的就是這樣的一個結(jié)果

namespace MySecondCS
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");
        }
    }
}

就是前面提到的,Hello World外面有一層Main函數(shù),Main函數(shù)外面有一個class,class外面有一個命名空間。

然后把之前寫過的Card類寫在Program前面,并將Main函數(shù)中的內(nèi)容改為

static void Main(string[] args)
{
    Card card = new Card() { Id = 1, Name = "A", Color = "紅桃" };
    card.introduce();
}

這樣啟動命令行,會得到上一節(jié)同樣的結(jié)果。

將類寫在一個文件中當然沒什么問題,但隨著所開發(fā)的應(yīng)用越來越復(fù)雜,涉及到的類也會越來越多,全部堆在一個文件中,缺乏有效的組織,顯然是不成的。

正所謂晴天帶傘、居安思危,全堆一個文件不行,那就把類寫在另一個文件中就是了。右鍵解決方案中的項目名,選擇添加類,如下圖

C#中面向?qū)ο蟮南嚓P(guān)知識點有哪些

類名取為Card,然后項目中除了Program.cs之外,還會出現(xiàn)一個Card.cs,其內(nèi)容為

namespace MySecondCS
{
    internal class Card
    {
    }
}

其中,namespace為命名空間,在同一個命名空間中的類可以互相調(diào)用。internal是一個用于修飾類的關(guān)鍵字,是對可訪問性的一種限制,這個限制并不強,只要在一個程序集中,就可以訪問。

這種訪問限制,在前面第一次創(chuàng)建Card的時候就有提過,Card類中,修飾成員變量用到的public也是用于訪問限制的。

接下來把之前寫好的Card代碼剪切進internal class Card中,在啟動命令行,程序仍然是可以跑通的。

繼承

之所以要有繼承這個概念,是因為紙牌的玩法太多了,比如我小的時候就喜歡搜集小當家的水滸卡,梁山好漢108將剛好是兩幅撲克牌,但是Card類中并沒有額外給梁山好漢提供位置。

這個時候就會遇到兩難問題,若直接把Card改成小當家水滸卡,那么打牌的人會覺得這玩意沒啥用,只會白白地浪費內(nèi)存;若是另起爐灶重新寫一個類,那老板會覺得你同樣的內(nèi)容寫兩遍,是不是欺負我不懂技術(shù)?然后說不定就扣工資了。

所以,繼承就比較好地解決了這個問題,就像這個名字暗示的,在C#中,可以新建一個水滸卡的類,可以在繼承Card類中的各種成員之外,添加自己獨有的成員。

接下來在Card類的下面,新建一個類,就叫UniCard,表示統(tǒng)一小當家水滸卡,如下所示

class UniCard : Card
{
    public int Order { get; set; }
    public string PersonName { get; set; }
}

其中,UniCard : Card就表示,前者是對后者的繼承,所有在Card中public的功能,都可以在UniCard中無痛調(diào)用。

接下來在UniCard中實現(xiàn)一個功能,即根據(jù)排名確定其對應(yīng)的紙牌面額。紙牌大小排序是大小王,然后是AkQJ,再然后是10到2。

那么2副撲克牌中,有4個大小王,其他諸如AKQJ之類的都有8張。現(xiàn)令大小王是0,A是1,那么其對應(yīng)的排序就是0->1->13->12->...->2。

也就是說,排名1, 2, 3, 4對應(yīng)撲克牌中的大小王,面額為0;5-12對應(yīng)撲克牌中的A,面額為1,然后接下來,每新增八位,其面額就加1。其函數(shù)實現(xiàn)為

int order2Id(uint order){
    if(order <= 4)
        return 0;
    else if (order <= 12)
        return 1;
    else
        return 14 - (order-4) / 8;
}

這個函數(shù)是非常簡單的,但接下來要將其嵌入到UniCard類中,實現(xiàn)通過Order自動生成Id這樣的功能

class UniCard : Card
{
    public int Order { get; set; }
    public string PersonName { get; set; }
    public void getIdName()
    {
        if (this.Order <= 4)
            this.Id = 0;
        else if (this.Order <= 12)
            this.Id = 1;
        else
            this.Id = 14 - (this.Order - 4) / 8;
        switch (this.Id)
        {
            case 0: this.Name = "Joker"; break;
            case 1:this.Name = "A"; break;
            case 13:this.Name = "K"; break;
            case 12:this.Name = "Q";break;
            case 11:this.Name = "J"; break;
            default: this.Name = this.Id.ToString(); break;
        }
    }
}

其中,this表示當前的這個class,在不引起歧義的情況下是可以省略的。所謂引起歧義,就是假如這個class外面已經(jīng)有了一個Name,那么在這個class里面如果非常突兀地來一個Name=1,可能會導致程序不知道這個Name到底指向誰。

另外,如果if后面跟著的程序塊中只有一行代碼,那么花括號可以省略。

除此之外,上面的代碼稍微長了一點,但并沒有新的知識點,只是相當于復(fù)習了一下switch case。

接下來,在Main函數(shù)中創(chuàng)建一個UniCard,并調(diào)用其繼承的自我介紹的函數(shù)。

static void Main(string[] args)
{
    UniCard uniCard = new UniCard();
    uniCard.Order = 6;
    uniCard.Color = "紅桃";
    uniCard.getIdName();
    uniCard.introduce();
}

運行之后,命令行輸出我的名字是紅桃A

6號如果我沒記錯的話,是豹子頭林沖,結(jié)果現(xiàn)在變成了紅桃A,看來這個繼承還是比較成功的。

枚舉

撲克牌的花色只有四種,紅桃、黑桃、草花、方片,如果把數(shù)據(jù)類型限制為字符串,保不準有人會把牌的花色定義為“五彩斑斕黑”之類的,為了做一個限制,目前想到比較好的方案是用字典

Dictionary<int, String> CARD_COLOR = new Dictionary<int, string>
{
    {0, "黑桃" },
    {1, "紅桃" },
    {2, "草花" },
    {3, "方片" }
};

然后再把花色定義為整型,想要看花色的時候以CARD_COLOR[0]這種形式調(diào)用。

這樣一來思路就打開了,甚至可以將花色封裝成字符串數(shù)組

String[] CARD_COLOR = new string[] { "黑桃", "紅桃", "草花", "方片" };

然而在C#中,其實有更加優(yōu)雅的解決方案,這個方案就是枚舉

public enum COLOR { 黑桃, 紅桃, 草花, 方片};

上面這行代碼可以寫在internal class Card的外面,然后在Card類中可以把花色定義為

public COLOR Color { get; set; }

枚舉這種數(shù)據(jù)類型的好處是,既有字符串的特點,又有整型的特點,以COLOR這種類型為例,黑桃對應(yīng)的是0,紅桃對應(yīng)的是1,以此類推。

這樣一來,getIdName這個函數(shù),除了可以通過排名來算牌的面額,還可以據(jù)此計算牌的花色。

public void getIdName()
{
    //...
    //寫在switch case后面
    if (this.Order <= 2)
        this.Color = COLOR.黑桃;
    else if (this.Order <= 4)
        this.Color = COLOR.紅桃;
    else
        this.Color = (COLOR)((this.Order - 5) % 8 / 2);
}

其中COLOR.黑桃是常用的枚舉類型的調(diào)用方法,而后面的(COLOR)相當于把其后面的(this.Order - 5) % 8 / 2這個整數(shù),強制轉(zhuǎn)化為枚舉類型。

改完這些之后,就會發(fā)現(xiàn)Main函數(shù)中的uniCard.Color = "紅桃";出現(xiàn)了紅色的下劃波浪線,說明出現(xiàn)了錯誤。原因也很簡單,現(xiàn)在的Color是枚舉類型,并不能賦值一個字符串。

將這行刪掉之后,再運行程序,命令行輸出為

我的名字是黑桃A

說明introduce中的$"我的名字是{Color}{Name}"仍然發(fā)生了作用,枚舉類型,通過6 66這個數(shù)值,計算得到了COLOR.黑桃這個結(jié)果,最后又通過$字符串轉(zhuǎn)化成了字符串。

這就是前文所言,枚舉類型,既有整型,又有字符串。

構(gòu)造函數(shù)和方法重載

現(xiàn)在回顧一下Main函數(shù),發(fā)現(xiàn)UniCard的創(chuàng)建過程未免太過繁瑣。

static void Main(string[] args)
{
    UniCard uniCard = new UniCard();
    uniCard.Order = 6;
    uniCard.getIdName();
    uniCard.introduce();
}

Main函數(shù)中的4行代碼中,如果只保留第一行和最后一行,那就完美了,比如寫成這種

UniCard uniCard = UniCard(6);
uniCard.introduce();

在這個過程中,UniCard變成了一個函數(shù),通過輸入一個排名,便可以初始化花色、牌額等內(nèi)容,這個函數(shù)就叫做構(gòu)造函數(shù),想要實現(xiàn),只需在UniCard中添加

public UniCard(int order)
{
    Order = order;
    getIdName();
}

需要注意,在Order=order中,前面的Order為類成員,其實可以寫為this.Order,getIdName也可以寫為this.getIdName,由于不會引起歧義,所以將this省略了。

這時會發(fā)現(xiàn),Main函數(shù)中又出現(xiàn)了錯誤:

UniCard uniCard = new UniCard();

這時因為,我們已經(jīng)為UniCard設(shè)定了唯一的構(gòu)造函數(shù),這個構(gòu)造函數(shù)必須要輸入一個整型才能執(zhí)行,UniCard()的參數(shù)卻空空如也,這不報錯才怪,解決方法也很簡單,只需將其改為我們喜聞樂見的形式就行了

static void Main(string[] args)
{
    UniCard uniCard = new UniCard(6);
    uniCard.introduce();
}

這個時候有人說了,那我就想生成一個啥也沒有的UniCard,你這么改來改去把我想要的改沒了,你還我UniCard()。

這個需求也是可以滿足的,這就是所謂的重載。所謂重載,就是在C#中,允許創(chuàng)建一些同名函數(shù),這些同名函數(shù)可以有著不同的輸入?yún)?shù),所以只需在public UniCard(int order)前面或者后面添加下面的代碼,就可以既滿足帶參數(shù)的構(gòu)造函數(shù),又滿足不帶參數(shù)的構(gòu)造函數(shù)了。

public UniCard(){}

運算符重載

無論是是打牌,還是梁山好漢,都是有排名的。有排名就可以比大小,比大小,就涉及到了大于號等于號小于號之類的東西。

正如字符串可以把加號更改為拼接的意思,Card也應(yīng)該有重新定義運算符的能力,這種能力就叫做運算符重載。

對于已經(jīng)建立起函數(shù)重載這種概念的人來說,運算符重載并不存在理解上的困難,畢竟運算符也是一種函數(shù)。

下面針對UniCard這種數(shù)據(jù)類型,對比較運算符進行重載,

public static bool operator< (UniCard a, UniCard b){
    return a.Order > b.Order;
}
public static bool operator>(UniCard a, UniCard b){
    return a.Order < b.Order;
}
public static bool operator==(UniCard a, UniCard b){
    return a.Order == b.Order;
}
public static bool operator!=(UniCard a, UniCard b){
    return a.Order != b.Order;
}

非常直觀,其中static為靜態(tài)類的修飾符,所謂靜態(tài)類型,表示在類尚未實例化時就可以調(diào)用,所有運算符重載函數(shù)都必須是static的。

operator<表示重新定義運算符<,bool類型標識作為運算結(jié)果的數(shù)據(jù)類型。

在這種排名中,肯定是數(shù)越小的人地位越高,所以排名第一大于排名第5,從而其Order大的反而小。

在進行了這些運算符重載之后,就可以在Main函數(shù)中進行調(diào)用了

static void Main(string[] args)
{
    UniCard a = new UniCard(15);
    UniCard b = new UniCard(25);
    if (a > b)
        Console.WriteLine($"{a.Color}{a.Name} > {b.Color}{b.Name}");
    else
        Console.WriteLine($"{a.Color}{a.Name} <= {b.Color}{b.Name}");
}

運行之后,命令行輸出為

紅桃K > 草花Q

“C#中面向?qū)ο蟮南嚓P(guān)知識點有哪些”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI