C# 基礎知識系列- 5 反射和泛型

来源:https://www.cnblogs.com/c7jie/archive/2020/03/31/12609508.html
-Advertisement-
Play Games

前言 為什麼要把反射和泛型放在一起講呢,這裡是處於個人對C 的一個很棒的觀感,因為C 的反射是可以獲取泛型里的元素的,而不像Java一個讓我比較難受的地方就是Java的泛型實際編譯的時候會擦除類型信息。 那麼問題來了,什麼是泛型,什麼又是反射呢? 泛型 請原諒我先介紹泛型,因為沒有泛型基礎直接介紹反 ...


前言

為什麼要把反射和泛型放在一起講呢,這裡是處於個人對C#的一個很棒的觀感,因為C#的反射是可以獲取泛型里的元素的,而不像Java一個讓我比較難受的地方就是Java的泛型實際編譯的時候會擦除類型信息。
那麼問題來了,什麼是泛型,什麼又是反射呢?

泛型

請原諒我先介紹泛型,因為沒有泛型基礎直接介紹反射是不完整的,就比如說你辛辛苦苦拿到一個類的反射信息,等用的時候才發現結果這是一個泛型類,那還得解析這個類的泛型的信息,這時候就必須先有一個泛型的基礎。
那麼什麼是泛型呢,先看看百度百科給的定義:

泛型是程式設計語言的一種特性。允許程式員在強類型程式設計語言中編寫代碼時定義一些可變部分,那些部分在使用前必須作出指明。各種程式設計語言和其編譯器、運行環境對泛型的支持均不一樣。將類型參數化以達到代碼復用提高軟體開發工作效率的一種數據類型。泛型類是引用類型,是堆對象,主要是引入了類型參數這個概念。

額,說實話哈,有一部分我沒看懂他寫的是啥。根據我的理解,泛型就是模板類里套的參數。就好比我們從網上找到一個好看的PPT模板,我們在寫PPT的時候根據我們的主題套用這個模板,然後寫出一個很好看的PPT,被老闆表揚升職加薪。嗯,事實上用好了泛型也會升職加薪。

泛型說的籠統一些就是類型參數化的過程,我們之前介紹的List就是一個泛型類。泛型分泛型類/介面和泛型方法。泛型類和泛型介面可以看做是一種,因為它的泛型參數是用在整個結構體裡面的(註意不是結構,struct);泛型方法又有參數泛型和返回值泛型兩種。

聲明一個泛型類/介面

public class Template<T>
{
	private T data;
	public void SetTemplate(T temp)
    {
    	data = temp;
    }
    public T GetTemplate()
    {
    	return data;
    }
}

上述示例是一個簡單的泛型類,體現了泛型類的特點。在聲明類的時候,聲明一個泛型占位符T ,在下麵的屬性、欄位、方法的參數和方法的返回值都可以使用這個占位符,約定類型一致。

泛型的介面和泛型類是一致的,只不過介面沒有方法的實現內容也就是方法體而已。

泛型類的使用

// 繼續上面的代碼
Template<int> temp = new Template<int>();
temp.SetTemplate(10);
int ten = temp.GetTemplate();

使用泛型類和普通類不同的地方就是,泛型類告訴編譯器你要傳遞的類型。使用<> 做標記,中間寫類型,表示這是一個泛型為XXX的泛型類。通常與其他語言不同的地方是,C#的泛型支持所有類型,意思就是在沒有額外聲明的時候,可以使用任意類型作為泛型參數傳遞。

泛型方法

C#也可以聲明一個方法為泛型方法,方法的泛型聲明是聲明在方法名的後面,參數列表的前方。

public void TemplateMethod<T>(T arg);
public T TemplateMethod1<T>();
public T TemplateMethod2<T>(T arg);

上述三個都是合規的泛型方法聲明。泛型可以是參數,也可以是返回值,還能既是返回值又是參數。

那麼問題來了,多個泛型參數該怎麼聲明?

如下:

public T2 TemplateMothod3<T1,T2>(T1 arg);
public T3 TemplateMothod4<T1,T2,T3>(T1 arg,T2 arg2);

在兩個尖括弧中間放入多個泛型,然後用逗號隔開,與參數列表和返回值的類型一一對應。

泛型方法的使用

TemplateMethod(10);// 方式 1
int it = TemplateMethod1<int>();// 方式 2

由於篇幅和時間的關係(主要是我寫這篇的時候時間有點晚了。。)就不對之前所有的方法進行演示了。

這裡簡單介紹一下泛型方法的使用:

  • 方式1 隱藏了一個泛型參數,這是因為如果泛型是參數的話,c#會根據參數的類型自動解析對應的泛型類型是什麼,方式1 等同於TemplateMethod<int>(10);
  • 方式2 當泛型參數是返回值時,必須告知具體的泛型類型。

泛型約束和泛型標記

約束

在實際開發過程中,我們會對一些泛型類的泛型參數進行類型約束,那麼泛型約束應該怎麼寫呢,看示例:

public void Demo<T>(T arg) where T : 約束內容
public void Demo<T,P>(T arg,P arg1) where T: 約束內容 where P:約束內容

如果對多個參數進行約束,就寫多個where。

泛型的約束有一下幾種:

  • class 表示這是個引用類型
  • new() 表示必須有一個無參構造函數
  • struct 表示是個結構體
  • 具體的類名或介面名 表示這個參數必須是這個類的子類或介面的實現類

泛型標記

在C#里有個很有意思的地方,那就是泛型標記。

泛型支持 in/out作為占位符T的前置標記。那這兩個標記是什麼意義呢,in表示這個類型參數只能作為參數列表的類型進行傳遞,out表示這是一個返回值的類型,示例如下:

public T2 Demo<in T1,out T2>(T1 t1);

類和方法的標記大同小易,基本上是一致的。

反射

反射在很多地方都有著使用,這裡先簡單的介紹一下C#中的反射相關內容,因為細講的話會涉及到很多東西而且還需要很多前置概念,不過在自己寫框架之前不需要涉及到太多反射的內容。

反射,英文名 reflect,簡單的介紹就是將類型對象化,然後操作這個對象的技術。

我們先創建一個示例類:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public Person()
    {
        Name = "小李";
        Age = 24;
    }

    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public string SayHi()
    {
        return "你好,我叫" + Name + "我的年紀是 " + Age;
    }
}

獲取一個類型對象

首先需要註意的一個類:Type,這個類是反射技術里的基石,甚至可以說是核心,表示一個類的類型信息。

那麼,我們該如何獲取類型對象呢?在C#中常見的有如下兩個方法:

  1. 使用typeof 關鍵字
Type personType = typeof(Person);
  1. 通過對象,使用GetType 方法
Person person = new Person();
Type personType = person.GetType();

如果我們在編寫程式的時候,知道要獲取什麼類的Type對象的話,建議使用typeof獲取。如果我們只有一個對象,需要通過這個對象進行操作的話,那麼最好使用GetType來獲取。

現在我們獲取到了一個Person類的Type對象,可以用來做什麼呢?

Type對象的用處

  1. 獲取類名:personType.Name
  2. 獲取所有屬性:personType.GetProperties()
  3. 獲取所有方法:personType.GetMethods()
  4. 獲取所有構造函數:personType.GetConstructors()

現在我們一一介紹一下這四種寫法:

第一條:顧名思義,獲取到的結果是Person 這個值。

第二條:該方法會返回一個類型為PropertyInfo[] 的數組,這個數組裡包含著所有使用public聲明的屬性。當然也可以通過指定的屬性名獲取屬性對象:personType.GetProperty("Name") 這裡會獲取到Person類的Name屬性。

第三條: 獲取該類所有public的方法,並將其封裝成一組類型是MethodInfo的對象數組。同理,也可以根據方法名進行檢索:personType.GetMethod("SayHi") ,就能獲取對應的SayHi方法。不過,如果有同名方法的話,就可能會出現獲取到的方法不是你想要的了。嗯,這部分會放到精講反射的時候再來細說。

第四條: 獲取構造函數,返回的是一個類型是ConstructorInfo的數組,表示所有的構造方法,不過可惜的是,沒有根據名字檢索的方法了,因為構造方法就一個名。

使用PropertyInfo動態操作一個對象的屬性值

我們通過上一小節獲取到了一個類的屬性PropertyInfo,現在可以利用這個屬性進行後續的操作:

Person person = new Person();
Type personType = person.GetType();
PropertyInfo prop = personType.GetProperty("Name");//獲取Name屬性
Object value = prop.GetValue(person);// 獲取 對象 person 的Name屬性值
prop.SetValue(person, "wangyipeng");// 為對象 person的Name屬性設置值為 wangyipeng

需要註意的是:

如果 類的屬性只有get,那麼在調用SetValue時會報錯。可能要問了,我們知道是有set,但是程式怎麼判斷呢?通過prop.CanWrite 的值進行判斷,如果值是true則表明這個屬性可以寫入值,否則不能。

同理,可以很輕易的聯想到如果只有set,那麼GetValue也會報錯,與之相對應的就是prop.CanRead屬性了。

使用MethodInfo手動執行一個對象的方法

首先,獲得到一個對象里的某一個方法:

Person person = new Person();
Type personType = person.GetType();
MethodInfo method = personType.GetMethod("SayHi");

現在獲取到了 方法對象,該怎麼執行呢?

MethodInfo有一個Invoke方法,這個方法有兩個重載版本。其中有一個是:Invoke(object obj, object[] parameters),第一個參數是要執行的方法所屬的對象,後面的數組參數是對應方法的參數列表,如果為空則填null即可。該方法有個返回值,類型是object,如果方法是沒有返回值的方法,那麼Invoke的返回值就是null。

通過反射獲取一個對象

通過反射獲取一個類的類型對象有幾種方式,先介紹一個不用類型的方式:

Person p = Activator.CreateInstance<Person>();

這種方式有一個要求,Person必須有一個無參的構造函數。

第二種方式:

Type personType = typeof(Person);
object p = Activator.CreateInstance(personType);//使用無參構造函數
p = Activator.CreateInstance(personType, "小王", 19);//使用Person(string,int)這個構造函數

當需要傳遞參數的時候,參數類型必須與對應的構造函數一一對應,如果順序變了,可能會出現找不到對應類的問題。

第三種:

//types 是參數列表的參數類型集合,順序與實際參數順序一致
ConstructorInfo cons = personType.GetConstructor(Type[] types);
/*
實際上應該是這個調用方
ConstructorInfo cos = personType.GetConstructor(new[]{ typeof(string), typeof(int)});
*/
object person  = cos.Invoke(new object[] {"王先生", 19});

這時候一個簡單的反射介紹就到這裡了,反射這裡還有一大篇的內容要將。這部分我會放到基礎篇完結之後再做一個統一介紹的。不過先道個歉,沒介紹泛型在反射的應用。

註:代碼里映射的王先生是我一個故人,最近與他有一些糾紛。
更多內容煩請關註我的博客

file


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 聽說隔壁用 Lombok 的六點就下班了,我也想六點下班! 好的,那麼這篇文章就介紹下 什麼是 Lombok , Lombok 做了什麼 以及 Lombok 是怎麼做的 ? 在介紹之前,先通過是否使用 Lombok 的效果來看下對比,首先來看下沒有 Lombok 之前,我們的一個簡單的 Java 對 ...
  • 在使用scrapy抓取網頁時, 如果遇到使用js動態渲染的頁面, 將無法提取到在瀏覽器中看到的內容. 針對這個問題scrapy官方給出的方案是scrapy selenium, 這是一個把selenium集成到scrapy的開源項目, 它使用selenium抓取已經渲染好(js代碼已經執行完成)的動態 ...
  • 調用三方介面返回值JSON字元串帶BOM頭"\ufeff",JSON解析死活報錯。 我是用SpringBoot的 調用三方介面的,一開始返回值我是用對象接收返回值,發現一直報錯,我以為是 的接收轉換有問題,就將返回值換成了 類型去接收。接收到字元串後再轉JSON、JSON字元串解析死活報錯。 介面返 ...
  • class Res { private String name; private int count = 1; private boolean flag; public synchronized void set(String name) { while (flag) { try { this.wa ...
  • title: Java基礎語法(7) 數組 blog: "CSDN" data: "Java學習路線及視頻" 1.數組的概述 數組(Array),是多個相同類型數據按一定順序排列的集合,並使用一個名字命名,並通過編號的方式對這些數據進行統一管理。 數組的常見概念 數組名 下標(或索引) 元素 數組的 ...
  • title: Java基礎語法(6) 註釋 blog: "CSDN" data: "Java學習路線及視頻" 用於註解說明解釋程式的文字就是註釋。 提高了代碼的閱讀性;調試程式的重要方法。 註釋是一個程式員必須要具有的良好編程習慣。 將自己的思想通過註釋先整理出來,再用代碼去體現 1.單行註釋 格式 ...
  • 一. 獲取多個單元格的值報錯:AttributeError: 'tuple' object has no attribute 'value' 需要讀取的sample.xlsx 代碼讀取的是A3:B10之間的單元格 運行結果: 二. 如何解決 上面報錯信息是,元組對象沒有屬性"value",我們先來看 ...
  • 原創聲明 本文作者:黃小斜 轉載請務必在文章開頭註明出處和作者。 什麼是消息隊列 “RabbitMQ?”“Kafka?”“RocketMQ?”...在日常學習與開發過程中,我們常常聽到消息隊列這個關鍵詞,可能你是熟練使用消息隊列的老手,又或者你是不懂消息隊列的新手,不論你了不瞭解消息隊列,本文都將帶 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...