06. C#入門系列【自定義類型】:從青銅到王者的進階之路

C#入門系列【自定義類型】:從青銅到王者的進階之路

一、引言:為什么需要自定義類型?

在C#的世界里,系統自帶的類型(如intstringbool)就像是基礎武器,能解決一些簡單問題。但當你面對復雜的業務場景時,就需要像英雄聯盟里的英雄一樣,打造屬于自己的"終極武器"——自定義類型。

比如,你要開發一個游戲,需要描述一個角色:

  • 只用系統類型:
    string name = "蓋倫";
    int level = 18;
    double health = 616.28;
    string[] skills = { "致命打擊", "勇氣", "審判", "德瑪西亞正義" };
    
  • 用自定義類型:
    var garen = new Hero { Name = "蓋倫", Level = 18, Health = 616.28,Skills = new[] { "致命打擊", "勇氣", "審判", "德瑪西亞正義" } 
    };
    

哪個更簡潔明了?一目了然!

二、自定義類型的"三劍客"

1. 類(Class):最全能的戰士

類就像是游戲中的全能型英雄,既能扛傷害(封裝數據),又能打輸出(提供方法)。

public class Hero
{// 字段:英雄的屬性private string _name;// 屬性:英雄的公開特性public string Name { get => _name; set => _name = value ?? throw new ArgumentNullException("名字不能為空"); }public int Level { get; set; } = 1; // 默認1級public double Health { get; set; }public string[] Skills { get; set; }// 方法:英雄的技能public void Attack() => Console.WriteLine($"{Name}使用了普通攻擊!");public void UseSkill(int skillIndex){if (skillIndex < 0 || skillIndex >= Skills?.Length){Console.WriteLine($"{Name}沒有這個技能!");return;}Console.WriteLine($"{Name}使用了技能:{Skills[skillIndex]}!");}
}

使用示例

var ezreal = new Hero
{Name = "探險家",Level = 15,Health = 520.36,Skills = new[] { "秘術射擊", "精華躍動", "奧術躍遷", "精準彈幕" }
};ezreal.UseSkill(3); // 輸出:探險家使用了技能:精準彈幕!

2. 結構(Struct):輕量級特種兵

結構就像是游戲中的特種兵,身材小巧(值類型),但行動迅速(無需堆內存分配)。

public struct Point
{public double X { get; set; }public double Y { get; set; }// 構造函數public Point(double x, double y) => (X, Y) = (x, y);// 方法:計算到原點的距離public double DistanceToOrigin() => Math.Sqrt(X * X + Y * Y);// 重寫ToString方法public override string ToString() => $"({X}, {Y})";
}

使用示例

var point = new Point(3, 4);
Console.WriteLine($"點{point}到原點的距離是:{point.DistanceToOrigin()}"); 
// 輸出:點(3, 4)到原點的距離是:5

類 vs 結構

特性類(引用類型)結構(值類型)
存儲位置堆(Heap)棧(Stack)或字段
拷貝方式引用拷貝值拷貝
默認初始值null各字段的默認值
適合場景復雜對象、需要繼承輕量級數據、頻繁創建銷毀

3. 枚舉(Enum):游戲中的技能欄

枚舉就像是游戲中的技能欄,把一組相關的值放在一起,方便選擇和使用。

public enum HeroType
{Warrior,    // 戰士Mage,       // 法師Assassin,   // 刺客Tank,       // 坦克Support,    // 輔助Marksman    // 射手
}// 為Hero類添加Type屬性
public class Hero
{// 其他成員保持不變...public HeroType Type { get; set; }
}

使用示例

var yasuo = new Hero
{Name = "亞索",Type = HeroType.Assassin,Skills = new[] { "斬鋼閃", "風之障壁", "踏前斬", "狂風絕息斬" }
};Console.WriteLine($"{yasuo.Name}是一名{yasuo.Type}型英雄。"); 
// 輸出:亞索是一名Assassin型英雄。// 更友好的輸出
string GetTypeName(HeroType type) => type switch
{HeroType.Warrior => "戰士",HeroType.Mage => "法師",HeroType.Assassin => "刺客",HeroType.Tank => "坦克",HeroType.Support => "輔助",HeroType.Marksman => "射手",_ => type.ToString()
};Console.WriteLine($"{yasuo.Name}是一名{GetTypeName(yasuo.Type)}型英雄。"); 
// 輸出:亞索是一名刺客型英雄。

三、進階技巧:讓自定義類型更強大

1. 繼承與多態:英雄的"覺醒"

通過繼承,你可以讓英雄獲得更強大的能力。

// 基類:所有英雄的共同特性
public abstract class BaseHero
{public string Name { get; set; }public int Level { get; set; } = 1;// 抽象方法:必須由子類實現public abstract void UltimateSkill();// 虛方法:子類可以重寫public virtual void Attack() => Console.WriteLine($"{Name}進行了普通攻擊。");
}// 子類:戰士英雄
public class WarriorHero : BaseHero
{public override void UltimateSkill() => Console.WriteLine($"{Name}使用了終極技能:神羅天征!");public override void Attack() => Console.WriteLine($"{Name}使用了戰技:突刺!");
}// 子類:法師英雄
public class MageHero : BaseHero
{public override void UltimateSkill() => Console.WriteLine($"{Name}使用了終極技能:隕石天降!");public override void Attack() => Console.WriteLine($"{Name}使用了魔法:火球術!");
}

多態演示

BaseHero[] heroes = new BaseHero[]
{new WarriorHero { Name = "趙云" },new MageHero { Name = "諸葛亮" }
};foreach (var hero in heroes)
{hero.Attack();hero.UltimateSkill();Console.WriteLine("----------------");
}// 輸出:
// 趙云使用了戰技:突刺!
// 趙云使用了終極技能:神羅天征!
// ----------------
// 諸葛亮使用了魔法:火球術!
// 諸葛亮使用了終極技能:隕石天降!
// ----------------

2. 接口:英雄的"裝備"

接口就像是游戲中的裝備,只要英雄"穿上"(實現),就能獲得相應的能力。

// 可遠程攻擊接口
public interface IRemoteAttack
{void RemoteAttack();
}// 可控制接口
public interface IControl
{void ControlEnemy();
}// 射手英雄:實現多個接口
public class MarksmanHero : BaseHero, IRemoteAttack, IControl
{public override void UltimateSkill() => Console.WriteLine($"{Name}使用了終極技能:萬箭齊發!");public void RemoteAttack() => Console.WriteLine($"{Name}進行了遠程攻擊!");public void ControlEnemy() => Console.WriteLine($"{Name}使用了控制技能:致盲!");
}

接口使用

var jinx = new MarksmanHero { Name = "金克絲" };
jinx.RemoteAttack();  // 金克絲進行了遠程攻擊!
jinx.ControlEnemy();  // 金克絲使用了控制技能:致盲!

3. 泛型:英雄的"萬能鑰匙"

泛型就像是游戲中的萬能鑰匙,可以適配各種場景。

// 裝備槽:可以存放任何類型的裝備
public class EquipmentSlot<T>
{private T _equipment;public void Equip(T equipment){Console.WriteLine($"裝備了:{equipment}");_equipment = equipment;}public T Unequip(){Console.WriteLine($"卸下了:{_equipment}");var temp = _equipment;_equipment = default;return temp;}
}// 使用示例
var weaponSlot = new EquipmentSlot<string>();
weaponSlot.Equip("無盡之刃");  // 裝備了:無盡之刃
var weapon = weaponSlot.Unequip();  // 卸下了:無盡之刃

四、常見陷阱與避坑指南

1. 結構的"甜蜜陷阱"

結構是值類型,頻繁裝箱拆箱會影響性能。

錯誤示范

var points = new List<Point>();
for (int i = 0; i < 1000000; i++)
{points.Add(new Point(i, i));  // 大量結構實例,可能導致性能問題
}

解決方案

  • 對于大數據量,優先使用類
  • 使用Span<T>Memory<T>避免裝箱

2. 枚舉的"數字游戲"

枚舉默認是int類型,可能導致意外賦值。

錯誤示范

HeroType type = (HeroType)99;  // 無效的枚舉值,但編譯通過

解決方案

  • 使用[Flags]特性創建位標志枚舉
  • 在使用前驗證枚舉值有效性
    if (!Enum.IsDefined(typeof(HeroType), type))
    {Console.WriteLine("無效的英雄類型!");
    }
    

3. 繼承的"深淵巨口"

過度繼承會導致代碼復雜度爆炸,就像游戲中技能點加錯了一樣。

錯誤示范

public class TankWarriorMageHero : BaseHero  // 多重職責,違反單一職責原則
{// ...
}

解決方案

  • 優先使用組合而非繼承
  • 遵循SOLID設計原則
  • 使用接口實現多角色能力

五、總結:自定義類型的"通關秘籍"

  1. 選擇合適的類型

    • 類:復雜對象,需要繼承和多態
    • 結構:輕量級數據,頻繁創建銷毀
    • 枚舉:固定值集合,提高代碼可讀性
  2. 善用高級特性

    • 繼承:擴展功能,實現多態
    • 接口:定義契約,實現橫向擴展
    • 泛型:提高代碼復用性
  3. 避開常見陷阱

    • 注意結構的性能問題
    • 驗證枚舉值的有效性
    • 避免過度繼承

掌握了這些,你就能在C#的世界里像職業選手一樣,靈活運用各種自定義類型,輕松應對各種復雜的業務場景,成為代碼界的"王者"!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/84523.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/84523.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/84523.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

使用 PyTorch 和 TensorBoard 實時可視化模型訓練

在這個教程中&#xff0c;我們將使用 PyTorch 訓練一個簡單的多層感知機&#xff08;MLP&#xff09;模型來解決 MNIST 手寫數字分類問題&#xff0c;并且使用 TensorBoard 來可視化訓練過程中的不同信息&#xff0c;如損失、準確度、圖像、參數分布和學習率變化。 步驟 1&…

第十五章 15.OSPF(CCNA)

第十五章 15.OSPF(CCNA) 介紹了大家都能用的OSPF動態路由協議 注釋&#xff1a; 學習資源是B站的CCNA by Sean_Ning CCNA 最新CCNA 200-301 視頻教程(含免費實驗環境&#xff09; PS&#xff1a;喜歡的可以去買下他的課程&#xff0c;不貴&#xff0c;講的很細 To be cont…

手機連接windows遇到的問題及解決方法

文章目錄 寫在前面一、手機與windows 連接以后 無法在win端打開手機屏幕,提示801方法零、檢查連接方法一、系統修復方法二、斷開重連方法三、軟件更新方法四、關閉防火墻 寫在前面 本文主要記錄所遇到的問題以及解決方案&#xff0c;以備后用。 所用機型&#xff1a;win11 專業…

Spring Boot + MyBatis Plus 項目中,entity和 XML 映射文件的查找機制

在 Spring Boot MyBatis - Plus 項目中&#xff0c;entity&#xff08;實體類&#xff09;和 XML 映射文件的查找機制有其默認規則&#xff0c;也可通過配置調整&#xff0c;以下詳細說明&#xff1a; 一、實體類&#xff08;entity&#xff09;的查找 MyBatis - Plus 能找到…

itvbox綠豆影視tvbox手機版影視APP源碼分享搭建教程

我們先來看看今天的主題&#xff0c;tvbox手機版&#xff0c;然后再看看如何搭建&#xff1a; 很多愛好者都希望搭建自己的影視平臺&#xff0c;那該如何搭建呢&#xff1f; 后端開發環境&#xff1a; 1.易如意后臺管理優化版源碼&#xff1b; 2.寶塔面板&#xff1b; 3.ph…

Vue Electron 使用來給若依系統打包成exe程序,出現登錄成功但是不跳轉頁面(已解決)

描述 用vue打成electron可執行exe程序時&#xff0c;發現個問題&#xff0c;一直登錄之后&#xff0c;頁面跳轉不了&#xff0c;其實后臺請求已成功發送 那么懷疑就是vue頁面跳轉的事情 解決 大部分vue 前段項目 會使用 js-cookie 這個庫 來操作瀏覽器的cookie 然而這個庫 …

Blob設置type為application/msword將document DOM節點轉換為Word(.doc,.docx),并下載到本地

core code // 導出為Word文檔downloadWord({ dom, fileName "", fileType "doc", l {} } {}) {l.show && l.show();// 獲取HTML內容const content dom.innerHTML;// 構建Word文檔的HTML結構const html <!DOCTYPE html><html>&l…

無需 Mac,使用Appuploader簡化iOS上架流程

作為開發者&#xff0c;尤其是從事跨平臺開發的團隊&#xff0c;iOS應用上架一直是一項繁瑣且挑戰重重的工作。盡管Flutter、React Native等框架使得我們可以在不同平臺之間共享代碼&#xff0c;iOS上架仍然是一個不可忽視的難題。因為它不僅僅涉及代碼構建&#xff0c;還涉及到…

【JVM】Java虛擬機(二)——垃圾回收

目錄 一、如何判斷對象可以回收 &#xff08;一&#xff09;引用計數法 &#xff08;二&#xff09;可達性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;標記清除 &#xff08;二&#xff09;標記整理 &#xff08;三&#xff09;復制 &#xff08;四&#xff…

Android 實現可拖動的ImageView

Android 實現可拖動的ImageView 代碼實現&#xff1a; public class DraggableImageView extends AppCompatImageView {private float lastTouchX;private float lastTouchY;public DraggableImageView(Context context) {super(context);init();}public DraggableImageView(C…

微信小程序中wxs

一、先新建wxs文件subutil.wxs 1、寫過濾器 //return class var isClass function(val) {if (val 0) {return grid-item} else if (val 1) {return temperature-error-slot} else if (val 2) {return chargingCycles-error-slot} else {return unrecognized-slot} } 2、…

Nginx攻略

&#x1f916; 作者簡介&#xff1a;水煮白菜王&#xff0c;一位前端勸退師 &#x1f47b; &#x1f440; 文章專欄&#xff1a; 前端專欄 &#xff0c;記錄一下平時在博客寫作中&#xff0c;總結出的一些開發技巧和知識歸納總結?。 感謝支持&#x1f495;&#x1f495;&#…

常見系統設計

秒殺系統 前端層&#xff1a; 靜態資源緩存&#xff1a;通過CDN緩存商品圖片、頁面靜態HTML&#xff0c;減少回源請求。 請求合并&#xff1a;合并用戶頻繁刷新的請求&#xff08;如10秒內僅允許一次真實請求&#xff09;。 端側限流&#xff1a;通過JS或APP端限制用戶高頻點擊…

git撤回commit

最常見的幾種撤回方式&#xff1a; 目標使用命令是否保留修改撤回最后一次 commit&#xff0c;但保留代碼修改git reset --soft HEAD~1? 保留撤回最后一次 commit&#xff0c;并丟棄修改git reset --hard HEAD~1? 丟棄撤回某個 commit&#xff0c;但保留后續提交git revert …

docker 安裝運行mysql8.4.4

先前一直使用mysql5.7&#xff0c;最新公司新項目&#xff0c;無意翻閱看下5.x版本mysql官方已經不再支持&#xff0c;于是準備選用MySQL8&#xff0c;官方8.4版本是個長期支持版本&#xff0c;選則最新版本8.4.4&#xff0c;如下是MySQL官方對版本支持計劃 MySQL版本下載查看地…

[java八股文][MySQL面試篇]索引

索引是什么&#xff1f;有什么好處&#xff1f; 索引類似于書籍的目錄&#xff0c;可以減少掃描的數據量&#xff0c;提高查詢效率。 如果查詢的時候&#xff0c;沒有用到索引就會全表掃描&#xff0c;這時候查詢的時間復雜度是On如果用到了索引&#xff0c;那么查詢的時候&a…

低代碼平臺的版本管理深度解析

引言 在當今快速發展的軟件開發領域&#xff0c;低代碼平臺憑借其可視化界面和拖拽功能&#xff0c;極大地減少了手動編碼的工作量&#xff0c;顯著提高了開發效率和質量。它提供了豐富的預構建模塊、組件和服務&#xff0c;讓開發者能夠根據業務需求和邏輯進行組合與配置&…

Springboot項目由JDK8升級至JDK17全過程教程【文末附源碼】

1. 前言 最近一直想把我的開源項目maple-boot升級到jdk17版本&#xff0c;然后接入Spring AI。拖延癥犯了一直拖拖拖&#xff0c;最近時間空閑較多&#xff0c;開始陸續著手升級。 整個升級過程 計劃分為3步。 step1&#xff1a;先將項目升級到jdk17&#xff0c;使用正常ste…

同步與異步:軟件工程中的時空藝術與實踐智慧-以蜻蜓hr人才系統舉例-優雅草卓伊凡

同步與異步&#xff1a;軟件工程中的時空藝術與實踐智慧-以蜻蜓hr人才系統舉例-優雅草卓伊凡 概念解析&#xff1a;時空維度的編程范式 在軟件開發的宇宙中&#xff0c;同步(Synchronous)與異步(Asynchronous)是兩種根本不同的執行模式&#xff0c;它們塑造了程序與時間和空間…

TF-IDF算法的代碼實踐應用——關鍵詞提取、文本分類、信息檢索

回顧&#xff1a;TF-IDF算法詳解與實踐總結 上一篇文章我們深入剖析了TF-IDF的原理與細節&#xff0c;但實踐才是檢驗真理的唯一標準&#xff01;今天&#xff0c;我們將從“紙上談兵”轉向“實戰演練”&#xff1a;通過純Python手寫實現與調用sklearn工具包兩種方式&#xff0…