網站首頁 編程語言 正文
引入
所謂泛型,就是創建一個函數,對所有數據類型都生效。最常見的例子就是運算符,畢竟1+1=2,1.0+1.0=2.0,足以看出+是對多種數據類型起作用的。
但是,如想創建一個函數add(int a, int b),那么輸入add(1.0, 1.0)是肯定要報錯的,VS直接就給標紅了。
泛型的出現,就很好地解決了這個尷尬的問題
T add<T>(T a, T b)
{
dynamic d1 = a;
dynamic d2 = b;
return (T)(d1 + d2);
}
Console.WriteLine(add<int>(1, 1));
Console.WriteLine(add<double>(1.0, 1.0));
上面代碼中,T表示某種數據類型,在調用函數add時,根據add后面的<>加以聲明。
但如果就此就寫return a+b顯然也是不行的,因為+這種運算符并沒有對T進行重載,編輯器并不會允許兩種未知的類型相加。
這個時候就需要用到dynamic,用來讓編輯器放棄類型檢查,將任何可能發生的錯誤都留給運行階段。
最后,運行結果為
2
2
類型約束
dynamic用著確實爽,但后果就是責任自負,這玩意要是用在團隊協作的場合,簡直就是災難,畢竟并非所有對象都可以駕馭加法。
所以,C#的泛型,是可以被約束的泛型,關鍵就是where,將上述代碼寫為
T add<T>(T a, T b) where T : struct{
dynamic d1 = a;
dynamic d2 = b;
return (T)(d1 + d2);
}
where T : struct表示T必須是數值類型的一種,所以編譯器的類型檢查仍會發揮作用,在調用add時,如果T不是數值類型,就會報錯。
C#一共有5種約束方案,列表如下
類別 | 條件 |
---|---|
struct | T必須是值類型 |
class | T必須是引用類型 |
new() | T必須有無參數的構造函數 |
基類名 | T必須是基類或派生自基類 |
接口名 | T必須是指定接口 |
裸類型 |
?不同類型的約束,或相同類型不同種類的約束,一般是可以混用的,如果不能混用,編譯器會提醒。比如struct幾乎不能和其他類型混用。如果new()參與了約束,則放在最后。
子類泛型
除了函數可以采用泛型,類當然也可以,不僅可以,而且還能繼承。
class MyList<T>
{
public T[] a;
public MyList(){} //無參數的構造函數,用于繼承
public MyList(int n){
a = new T[n];
}
public T this[int index]{
get => a[index];
set => a[index] = value;
}
???????}
MyList相當于是給數組套了一層殼,其構造函數并不存在什么難以理解的地方,唯一有些問題的可能是下面的索引器public T this[int index],這種寫法可以實現方括號形式的索引。
可以測試一下
var a = new MyList<int>(5);
for (int i = 0; i < 5; i++)
{
a[i] = i;
Console.WriteLine(a[i]);
}
結果就不粘貼了,接下來新建一個子類
class MyStack<T> : MyList<T>
{
public MyStack(int n)
{
a = new T[n];
}
public T Pop()
{
T p = a[a.Length- 1];
a = a[0..(a.Length-1)];
return p;
}
}
然后測試一下
var a = new MyStack<int>(3);
for (int i = 0; i < 3; i++)
{
a[i] = i;
}
for (int i = 0; i < 3; i++)
{
Console.WriteLine(a.Pop());
}
結果為
2
1
0
常用的泛型數據結構
C#通過泛型定義了很多數據結構,例如在講解switch...case時提到的字典
Dictionary<int, string> card = new Dictionary<int, string>
{
{1,"A" },
{11, "J" },
{12, "Q" },
{13, "K" }
};
這種<U, V>的寫法,正是泛型的特點,其中U, V就是可以隨意聲明的變量。如果查看字典的類型參數,可以發現其定義方法是這樣的
public class Dictionary<TKey, TValue> : ICollection<KeyValuePair<TKey, TValue>>, ... where TKey : notnull
考慮到本節并不是為了將面向對象,所以字典繼承的那一大坨類就省略了,關鍵是where Tkey:notnull,也就是說,字典對鍵值對的要求只有一個,就是鍵不得為null。
除了字典之外,還有一些常見的數據結構采用了泛型,列表如下,沒事兒可以練習練習。
數據結構 | 說明 | 常用方法 |
---|---|---|
List<T> | 泛型列表 | Add, Remove, RemoveAt |
LinkedList<T> | 雙端鏈表 | AddFirst, AddLast, RemoveFirst, RemoveLast |
Queue<T> | 先進先出列表 | Enqueue, Dequeue |
Stack<T> | 棧,先進后出 | Push, Pop |
泛型委托
委托,是函數的函數;泛型,可以讓函數的參數類型更加靈活,二者結合在一起,就是更加靈活的函數的函數,即泛型委托。
只要學過了泛型和委托,那么對泛型委托將毫無理解上的難度,回想前面定義的運算符委托
delegate int Op(int a, int b);
再回想定義泛型時的<T>,那么泛型委托可以非常簡單地定義出來
delegate T Op<T>(T a, T b);
然后就可以根據委托,建立一個泛型函數
T add<T>(T a, T b)
{
dynamic d1 = a;
dynamic d2 = b;
return (T)(d1 + d2);
}
var addTest = new Op<int>(add<int>);
//也可以省略add后的<int>,寫成下面的形式
//var addTest = new Op<int>(add);
Console.WriteLine(addTest(3, 5));
運行之后控制臺出現了8,就是這么簡單。
原文鏈接:https://tinycool.blog.csdn.net/article/details/128918277
- 上一篇:沒有了
- 下一篇:沒有了
相關推薦
- 2023-10-30 docker拉取鏡像時報錯Error response from daemon: Head ““no
- 2021-11-22 C++?STL中五個常用算法使用教程及實例講解_C 語言
- 2023-12-09 如何使用Python核對文件夾內的文件
- 2022-06-25 C#將DataGridView中的數據保存到CSV和Excel中_C#教程
- 2022-09-18 C++如何判斷一個數是不是素數_C 語言
- 2022-04-01 將numpy array保存為nii格式 itk-snap打不開
- 2023-07-09 前端axios請求,傳遞數組的時候會在url的后邊加中括號[]
- 2022-08-13 DHCP服務簡介及Linux配置實例
- 欄目分類
-
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支