net 自定義泛型那點事
泛型概述
泛型是程序設計語言的一種特性。允許程序員在強類型程序設計語言中編寫代碼時定義一些可變部分,那些部分在使用前必須作出指明。各種程序設計語言和其編譯器、運行環境對泛型的支持均不一樣。將類型參數化以達到代碼復用提高軟件開發工作效率的一種數據類型。泛型類是引用類型,是堆對象,主要是引入了類型參數這個概念。
泛型定義
泛型的定義主要有以下兩種: 1.在程序編碼中一些包含類型參數的類型,也就是說泛型的參數只可以代表類,不能代表個別對象。(這是當今較常見的定義) 2.在程序編碼中一些包含參數的類。其參數可以代表類或對象等等。(人們大多把這稱作模板)不論使用哪個定義,泛型的參數在真正使用泛型時都必須作出指明。 一些強類型編程語言支持泛型,其主要目的是加強類型安全及減少類轉換的次數,但一些支持泛型的編程語言只能達到部分目的。
正文
加入我想輸出double ,int 和 dateTime類型的類型名字和他們的值。現在需要定義方法如下
using System; using System.Data; namespace testData { class Program { static void Main(string[] args) { ShowInt(2); ShowDateTime(DateTime.Now); ShowDouble(2.52); Console.Read(); } public static void ShowInt(int value) { Console.WriteLine($"typeName:{value.GetType().Name},Value:{value}"); } public static void ShowDouble(double value) { Console.WriteLine($"typeName:{value.GetType().Name},Value:{value}"); } public static void ShowDateTime(DateTime value) { Console.WriteLine($"typeName:{value.GetType().Name},Value:{value}"); } } }
這樣寫起來,好像比較麻煩,我們可不可以用一個方法來代替呢???
我們很多初學者都接觸過這兩個泛型,
List<int> list = new List<int>();
Dictionary<int, string> dic = new Dictionary<int, string>();
第一個是列表,第二個我們稱為字典,那麽我們可不可以自己定義一個泛型方法呢????
泛型的語法結構及定義
泛型語法: 泛型類或方法的後面 “<T>” T 是占位符,可以是任意字母,主要代表一個類型,如果是方法,參數就應當為占位符參數。 如 Show<T>(T t);方法,User<T> 類 。
泛型的特點:1.延遲聲明,2使用的時候在聲明
1.泛型方法的定義和使用
using System; using System.Collections.Generic; using System.Data; namespace testData { class Program { static void Main(string[] args) { Show<int>(1); Show<double>(1.904); Show<DateTime>(DateTime.Now); Show<string>("wbc"); Console.Read(); } public static void Show<T>(T t) { Console.WriteLine($"typeName:{t.GetType().Name},Value:{t}"); } } }
我們會發現,完全可以運行,一個方法就搞定.那麽我們可不可以省略 <int> ,答案是肯定的,當沒有涉及到裝箱拆箱操作的時候,我們完全可以省略,如下
using System; using System.Collections.Generic; using System.Data; namespace testData { class Program { static void Main(string[] args) { Show(1); Show (1.904); Show(DateTime.Now); Show("wbc"); User u = new User(); Show(u); Console.Read(); } public class User { } public static void Show<T>(T t) { Console.WriteLine($"typeName:{t.GetType().Name},Value:{t}"); } } }
有人會說了,我使用Object 作為參數,一樣可以實現,為什麽不使用object呢,一切類型的父類是object ,應當可以的啊??,答案是肯定的,也是可以的,知所以不使用,是因為使用object類型的時候,會產生裝箱拆箱操作,這個操作會損失精度。
2泛型類的定義和使用
泛型類的語法和泛型方法的語法很像,只是關鍵字是class ,如 Public Class MyGer<T>{
}
那麽我們來看下泛型類的使用,看如下案列:
using System; using System.Collections.Generic; using System.Data; namespace testData { class Program { static void Main(string[] args) { Person p = new Person() { Name="zhangsan",Age=28,Id=1,Id_No="110000198212193145",Address="beijiang",Sex="女" }; MyGrenric<Person> gren = new MyGrenric<Person>(); gren.Show(p); Console.Read(); } } /// <summary> /// 人的父類 /// </summary> public class BasePopleModel { public string Name { get; set; } public int Age { get; set; } public string Address { get; set; } public string Id_No { get; set; } public string Sex { get; set; } } public class Pople { public string Name { get; set; } public int Age { get; set; } public string Address { get; set; } public string Id_No { get; set; } public string Sex { get; set; } public override string ToString() { string result = "{"+$"name:{this.Name},age : {this.Age},sex : {this.Sex},no : {this.Id_No} "+"}"; return base.ToString(); } } //人的信息 public class Person : BasePopleModel { public int Id { get; set; } public override string ToString() { string result = "{ "+ $"name : {base.Name},age : {base.Age},sex : {base.Sex},no : {base.Id_No} , id : {this.Id}"+" }"; return result; } } public class MyGrenric<T> { public void Show(T t) { Console.WriteLine(t); } } }
看了上面的代碼,我們可以看出,泛型類裏面的方法,可以是普通方法和泛型方法,當普通方法使用泛型的時候,我們可以省略泛型,上訴代碼替換為:
public void Show<T>(T t) { Console.WriteLine(t); }
是一樣的結果。但是泛型類也有一個弊端,那就是失去了繼承類的繼承性質,如
BasePopleModel p = new Person()
{
Name="zhangsan",Age=28,Id=1,Id_No="110000198212193145",Address="beijiang",Sex="女"
};
MyGrenric<Person> gren = new MyGrenric<Person>();
gren.Show(p);
這個時候,我們的代碼就會報錯。那如何在泛型中保留類的基礎性,使用如下語法就能做到:
View Code當我們所有繼承了BasePopleModel的子類,都可以使用這個泛型類。
3泛型接口的定義和使用
我們來看下代碼,定義一個泛型接口,和定義泛型類的語法結構是一樣的,他本身也具有接口的特性。代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApp3 { public interface IBaseImpl<T> { int State { get; set; } void Show(T t); void Search<S>(S s,T t); } }
當我們普通類繼承這個泛型接口的時候,我們會發現,繼承不了,生成編譯項目的時候,會提示出錯誤信息:如下圖
那我們來試試,泛型類來繼承泛型接口,看好使不好使,把我們上面創建的泛型類繼承我們的接口(MyGrenric<T>:IBaseImpl<T>)如下:
public class MyGrenric<T>:IBaseImpl<T> { public int State { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } public void Search<S>(S s, T t) { throw new NotImplementedException(); } public void Show(T t) { Console.WriteLine(t); } }
我們發現是能運行的.
現在問題來了,剛才我們的實例類繼承泛型接口繼承不了,我們的實例類繼承泛型類能繼承嗎??,有沒有什麽辦法,讓我們的實例類繼承我們的泛型接口和泛型類呢?我們先來看看,能不能實例類繼承泛型類
我們會發現還是繼承不了:既然前邊說了,T只是一個占位符,我們可不可以顯示的寫出一個類呢??
我們發現,這樣是可以的,沒有編譯錯誤,那麽我泛型類和泛型接口的占位符都是T,那麽我們使用不同 的類可以嗎??,答案是否定的,絕對不可以,一個占位符只能代表一個類型,所以我們要使用不同的類型,就需要使用不同的占位符,如:
泛型約束
前邊學習了這麽多自定義泛型的知識,我們基本就把整個泛型學習完了,我們之前一直都是說T,S,是泛型的一個占位符,可以是任意類型,那我們可以限定這個類型嗎?答案是肯定的,繼續看圖片
通過上訴,我們能看出,我們限定了類型,只能是Pople 類類型。限定語法,“只有在泛型類和接口之後跟WHERE 泛型占位符 :類型”。
where T: 類型值 | 說明 | 限定規範 |
class | 限定泛型只能是class 類型 | 可以有參數構造函數或無參數構造函數,不能和其他關鍵字一起使用 |
struct | 限定泛型只能是struct類型 | 不可以和其他類型一起使用 |
new() | 限定只能是類類型,切有無參數構造函數 | 必須有參數構造函數,不能和其他關鍵字一起使用 |
類類型 | 如果傳入的是父類,則保留繼承性質 | 無 |
值類型 | 無 | 無 |
這裏就不過多演示上述內容,我們在這裏只演示class ,代碼如下
using System; using System.Collections.Generic; using System.Data; namespace testData { class Program { static void Main(string[] args) { BasePopleModel p = new Person() { Name="zhangsan",Age=28,Id=1,Id_No="110000198212193145",Address="beijiang",Sex="女" }; MyGrenric<BasePopleModel> gren = new MyGrenric<BasePopleModel>(); gren.Show(p); Console.Read(); } } /// <summary> /// 人的父類 /// </summary> public class BasePopleModel { public string Name { get; set; } public int Age { get; set; } public string Address { get; set; } public string Id_No { get; set; } public string Sex { get; set; } } public class Pople { public string Name { get; set; } public int Age { get; set; } public string Address { get; set; } public string Id_No { get; set; } public string Sex { get; set; } public override string ToString() { string result = "{"+$"name:{this.Name},age : {this.Age},sex : {this.Sex},no : {this.Id_No} "+"}"; return base.ToString(); } } //人的信息 public class Person: BasePopleModel { public Person() { } public int Id { get; set; } public void Show(BasePopleModel s) { throw new NotImplementedException(); } public override string ToString() { string result = "{ "+ $"name : {base.Name},age : {base.Age},sex : {base.Sex},no : {base.Id_No} , id : {this.Id}"+" }"; return result; } } public class MyGrenric<T> where T : class ,new() { public void Show(T t) { Console.WriteLine(t); } } //public interface IBaseImpl<S> where S : class //{ // int State { get; set; } // void Show(S s); //} }
總結:泛型類必須繼承在泛型類上,如果作為普通類的父類,必須顯示指定其類型,在約束的時候,泛型類不能指定值類型的約束。
泛型接口必須被泛型類和泛型接口繼承,如果被普通接口和普通類繼承,必須顯示的指定類型,必須放在多個繼承文件的最後。在約束的時候,不能使用new()。
本文只是介紹常用的泛型使用方向,很多其他方向沒有詳細介紹,常用就是泛型類的使用,並不怎麽涉及到泛型的繼承,如果一個項目涉及到泛型繼承,證明這個項目也是快重構了。不過在開發過程中,泛型的約束是經常使用的。僅供參考
net 自定義泛型那點事