1. 程式人生 > >C#的擴充套件方法詳解

C#的擴充套件方法詳解

擴充套件方法被定義為靜態方法,但它們是通過例項方法語法進行呼叫的。 它們的第一個引數指定該方法作用於哪個型別,並且該引數以 this 修飾符為字首。 擴充套件方法當然不能破壞面向物件封裝的概念,所以只能是訪問所擴充套件類的public成員。
 擴充套件方法使您能夠向現有型別“新增”方法,而無需建立新的派生型別、重新編譯或以其他方式修改原始型別。擴充套件方法是一種特殊的靜態方法,但可以像擴充套件型別上的例項方法一樣進行呼叫。
C#擴充套件方法第一個引數指定該方法作用於哪個型別,並且該引數以 this 修飾符為字首。
擴充套件方法的目的就是為一個現有型別新增一個方法,現有型別既可以是int,string等資料型別,也可以是自定義的資料型別。
為資料型別的新增一個方法的理解:一般來說,int資料型別有個Tostring的方法,就是把int 資料轉換為字串的型別,比如現在我們想在轉換成字串的時候還新增一點東西,比如增加一個字元 a .那麼之前的Tostring就不好使了,因為它只是它我們的int資料轉換為string型別的,卻並不能新增一個字母 a.所以這就要用到所謂的擴充套件方法了。
首先我們看一個給現有的型別增加一個擴充套件方法。
我們想給string 型別增加一個Add方法,該方法的作用是給字串增加一個字母a.

例項1:

  1. //必須是靜態類才可以新增擴充套件方法
  2. Static class Program
  3. {
  4. static void Main(string[] args)
  5. {
  6. string str = "quzijing";
  7. //注意呼叫擴充套件方法,必須用物件來呼叫
  8. string Newstr = str.Add();
  9. Console.WriteLine(Newstr);
  10. Console.ReadKey();
  11. }
  12. //宣告擴充套件方法
  13. //擴充套件方法必須是靜態的,Add有三個引數
  14. //this 必須有,string表示我要擴充套件的型別,stringName表示物件名
  15. //三個引數this和擴充套件的型別必不可少,物件名可以自己隨意取如果需要傳遞引數,//再增加一個變數即可
  16. public static string Add(this string stringName)
  17. {
  18. return stringName+"a";
  19. }
  20. }</span>

我們再來嘗試給我們自定義的型別增加一個擴充套件方法,並增加一個傳遞的引數。

例項2:

首先我們宣告一個Student類,它包含了兩個方法StuInfo,getStuInfo.例項程式碼如下:

  1. public class Student
  2. {
  3. public string StuInfo()
  4. {
  5. return "學生基本資訊";
  6. }
  7. public string getStuInfo(string stuName, string stuNum)
  8. {
  9. return string.Format("學生資訊:\n" + "姓名:{0} \n" + "學號:{1}", stuName, stuNum);
  10. }
  11. }</span>

之後我們再宣告一個名為ExtensionStudentInfo的靜態類,注意必須為靜態

這個類的作用就是包含一些我們想要擴充套件的方法,在此我們宣告兩個Student型別的擴充套件方法,Student型別為我們自定義的型別。示例程式碼如下:

  1. public static class ExtensionStudentInfo
  2. {
  3. //宣告擴充套件方法
  4. //要擴充套件的方法必須是靜態的方法,Add有三個引數
  5. //this 必須有,string表示我要擴充套件的型別,stringName表示物件名
  6. //三個引數this和擴充套件的型別必不可少,物件名可以自己隨意取如果需要傳遞引數,再增加一個變數即可
  7. public static string ExtensionStuInfo(this Student stuName)
  8. {
  9. return stuName.StuInfo();
  10. }
  11. //宣告擴充套件方法
  12. //要擴充套件的方法必須是靜態的方法,Add有三個引數
  13. //this 必須有,string表示我要擴充套件的型別,stringName表示物件名
  14. //三個引數this和擴充套件的型別必不可少,物件名可以自己隨意取如果需要傳遞引數,在此我們增加了兩個string型別的引數
  15. public static string ExtensionGetStuInfo(this Student student, string stuname, string stunum)
  16. {
  17. return student.getStuInfo(stuname, stunum)+"\n讀取完畢";
  18. }
  19. }</span>

以上的工作做完之後便可以使用我們的擴充套件方法了,注意必須要用物件來呼叫擴充套件方法。
  1. static void Main(string[] args)
  2. {
  3. Student newstudent = new Student();
  4. //要使用物件呼叫我們的擴充套件方法
  5. string stuinfo = newstudent.ExtensionStuInfo();
  6. Console.WriteLine(stuinfo);
  7. //要使用物件呼叫我們的擴充套件方法
  8. string stuinformation = newstudent.ExtensionGetStuInfo("quzijing", "20081766");
  9. Console.WriteLine(stuinformation);
  10. Console.ReadKey();
  11. }</span>

例項3、使用TagBuilder類建立擴充套件方法

上面自定義的Span()方法十分簡單, 但是有時候我們要構造具有複雜結構的Html元素, 如果用字串拼接的方法就有些笨拙.

ASP.NET MVC框架提供了一個幫助我們構造Html元素的類:TagBuilder

TagBuilder類有如下方法幫助我們構建Html控制元件字串:

方法名稱用途
AddCssClass()新增class=””屬性
GenerateId()新增Id,  會將Id名稱中的"."替換為IdAttributeDotReplacement 屬性值的字元.預設替換成"_"
MergeAttribute()新增一個屬性,有多種過載方法.
SetInnerText()設定標籤內容, 如果標籤中沒有再巢狀標籤,則與設定InnerHTML 屬性獲得的效果相同.
ToString()輸出Html標籤的字串, 帶有一個引數的過載可以設定標籤的輸出形式為以下列舉值:
  • TagRenderMode.Normal -- 有開始和結束標籤
  • TagRenderMode.StartTag -- 只有開始標籤
  • TagRenderMode.EndTag -- 只有結尾標籤
  • TagRenderMode.SelfClosing -- 單標籤形式,如<br/>

同時一個TagBuilder還有下列關鍵屬性:

屬性名稱用途
AttributesTag的所有屬性
IdAttributeDotReplacement新增Id時替換"."的目標字元
InnerHTMLTag的內部HTML內容
TagNameHtml標籤名, TagBuilder只有帶一個引數-TagName的建構函式.所以TagName是必填屬性
下面在新增一個自定義的HtmlHelper類擴充套件方法,同樣是輸出一個<Span>標籤:
  1. public static string Span(this HtmlHelper helper, string id, string text, string css, object htmlAttributes)
  2. {
  3. //創意某一個Tag的TagBuilder
  4. var builder = new TagBuilder("span");
  5. //建立Id,注意要先設定IdAttributeDotReplacement屬性後再執行GenerateId方法.
  6. builder.IdAttributeDotReplacement = "-";
  7. builder.GenerateId(id);
  8. //新增屬性
  9. builder.MergeAttributes(new RouteValueDictionary(htmlAttributes));
  10. //新增樣式
  11. builder.AddCssClass(css);
  12. //或者用下面這句的形式也可以: builder.MergeAttribute("class", css);
  13. //新增內容,以下兩種方式均可
  14. //builder.InnerHtml = text;
  15. builder.SetInnerText(text);
  16. //輸出控制元件
  17. return builder.ToString(TagRenderMode.Normal);
  18. }</span>

在頁面上,呼叫這個方法:

<% =Html.Span("span.test", "使用TagBuilder幫助構建擴充套件方法", "ColorRed", new { style="font-size:15px;" })%>

生成的Html程式碼為:

<span id="span-test" class="ColorRed" style="font-size: 15px;">使用TagBuilder幫助構建擴充套件方法</span>

例項4

(1)、擴充套件方法

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Text.RegularExpressions;
  6. //宣告擴充套件方法的步驟:類必須是static,方法是static,
  7. //第一個引數是被擴充套件的物件,前面標註this。
  8. //使用擴充套件方法的時候必須保證擴充套件方法類已經在當前程式碼中using
  9. namespace 擴充套件方法
  10. {
  11. //擴充套件方法必須是靜態的
  12. public static class StringHelper
  13. {
  14. //擴充套件方法必須是靜態的,第一個引數必須加上this
  15. public static bool IsEmail(this string _input)
  16. {
  17. return Regex.IsMatch(_input, @"^\\[email protected]\\w+\\.\\w+$");
  18. }
  19. //帶多個引數的擴充套件方法
  20. //在原始字串前後加上指定的字元
  21. public static string Quot(this string _input, string _quot)
  22. {
  23. return _quot + _input + _quot;
  24. }
  25. }
  26. }
  27. </span>

(2)、使用方法
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. namespace 擴充套件方法
  6. {
  7. class Program
  8. {
  9. static void Main(string[] args)
  10. {
  11. string _myEmail = "[email protected]";
  12. //這裡就可以直接使用string類的擴充套件方法IsEmail了
  13. Console.WriteLine(_myEmail.IsEmail());
  14. //呼叫接收引數的擴充套件方法
  15. Console.WriteLine(_myEmail.Quot("!"));
  16. Console.ReadLine();
  17. }
  18. }
  19. }
  20. </span>

總結:

在我們的程式設計生涯中我們要使用很多很多類庫,這些類庫有的是我們自己開發的,我們有她的程式碼,有的是第三方釋出的,我們不僅沒有他們的程式碼,連看的機會都沒有。

作為.net程式設計師,我們每天都要和BCL(Base Class Linbrary)打交道。無疑,BCL做為一個年輕的框架類庫,她是成功的,但是還有一些時候我們還是得寫一些”Helper”方法來擴充套件類庫,由於我們不能修改類庫的原始碼,我們只有寫一個個的靜態類。雖然在使用上也算方便,但作為追求完美的程式設計師來說總有些不雅。 現在我就碰到這樣的事情,前兩天奉命寫一個從XML檔案載入Chart圖的設定的方法,從XML載入資料繫結到物件上,這肯定是反射的用武之地了。我經常需要寫一些根據物件屬性名字來判斷這個物件是否有這個屬性或者根據屬性名獲取該屬性的值。還是按照平常一樣,我很快寫了一個PropertyHelper,裡面有兩個靜態方法:HasProperty,GetValueByName。

PropertyHelper.HasProperty(point, "X"),如此的呼叫也還過得去,不過在C# 3.0微軟為我們提供了擴充套件方法。現在我們可以直接這樣呼叫了point.HasProperty(“X”);看看我是如何實現這個擴充套件方法的?

  1. public static class PropertyExtension
  2. {
  3. public static object GetValueByName(this object self, string propertyName)
  4. {
  5. if (self == null)
  6. {
  7. return self ;
  8. }
  9. Type t = self.GetType();
  10. PropertyInfo p = t.GetProperty(propertyName);
  11. return p.GetValue(self, null);
  12. }
  13. }</span>

我給object型別添加了一個擴充套件方法,在.net裡所有的類都繼承自object,那所有的類都預設的擁有這個方法了,真方便,呵呵。

注意到和普通的靜態方法有何差別?在這個方法的第一個引數前面多了一個this關鍵字。

擴充套件方法:

1 方法所在的類必須是靜態的

2 方法也必須是靜態的

3 方法的第一個引數必須是你要擴充套件的那個型別,比如你要給int擴充套件一個方法,那麼第一個引數就必須是int。

4 在第一個引數前面還需要有一個this關鍵字。

按照上面的步驟寫你就得到了一個“擴充套件方法”,你可以像呼叫這個類的原生方法那樣去呼叫它:

string str ="abc";
object len = str.GetValueByName("Length");

好像string型別現在有了GetValueByName這個方法一樣,但實際上string並沒有這樣一個方法。那這又是為什麼呢?是我們可愛的編譯器在其中做了手腳。為了避開編譯器的干擾,我們來直接欣賞MSIL程式碼:

L_0008:ldstr"Length"L_000d:callobject TestLambda.PropertyExtension::GetValueByName(objectstring)

從MSIL中我們可以看出,這段程式碼編譯後和呼叫靜態方法沒有任何的差別(從call指令來看,這是在呼叫一個靜態方法)。

從這裡可以知道擴充套件方法即可以使用例項呼叫的方式也可以直接使用靜態類呼叫的方式:

str.GetValueByName("Length");

PropertyExtension.GetValueByName(str,"Length");

下面將對擴充套件方法做一些細節的介紹:

Visual Studio 2008對擴充套件方法有智慧感知的支援,如下圖:

在方法的圖示上有一個與其他的都不相同,他的突變下面還帶有一個藍色的向下的箭頭,這就表明這個方法是一個擴充套件方法。

下面是對編寫擴充套件方法要注意的幾個原則(當然,仁者見仁、智者見智,這也是一家之言):

擴充套件方法有就近原則,也就是如果在你的程式裡有兩個一模一樣的擴充套件方法,一個和你的使用類是處於同一名稱空間裡,另外一個處於別的名稱空間裡,這個時候會優先使用同一名稱空間裡的擴充套件方法,也就是說“血緣關係”越近,越被青睞。

很多人看到擴充套件方法也許眼裡冒出金光,以後在設計的時候不管三七二十一,反正可以擴充套件。還有一些人會對類任意擴充套件,將以前一些作為”Helper”的方法統統使用擴充套件方法代替,注意的是擴充套件方法有“汙染性”,所以我覺得在擴充套件的時候還是想想,是不是值得這樣擴充套件。

在擴充套件的時候也不要對比較高層的類進行擴充套件,像我上面對object的擴充套件我覺得就是不可取的,object是所有類的基類,一經擴充套件,所有的類都被“汙染”了。

相關推薦

C#的擴充套件方法

擴充套件方法被定義為靜態方法,但它們是通過例項方法語法進行呼叫的。 它們的第一個引數指定該方法作用於哪個型別,並且該引數以 this 修飾符為字首。 擴充套件方法當然不能破壞面向物件封裝的概念,所以只能是訪問所擴充套件類的public成員。 擴充套件方法使您能夠向現有型別“新

C/C++混合程式設計--extern “C” 使用方法

1. 首先要明白: 被extern “C”修飾的變數和函式是按照C語言方式編譯和連結的 (1) 首先看看C++中對類似C的函式是怎樣編譯的。 C++支援函式過載,而過程式語言C則不支援。函式被C++編譯後在符號庫中的名字與C語言的不同。例如,假設某個函式的原型為: void foo( i

C# sort 方法及示例

諸如List<T>等泛型集合類,直接提供了sort()方法用於將集合中的元素進行排序。 但是,其前提是集合中存放的是可直接排序的基本型別,如List<int>, List<double>,如果 我們定義了一個自定義型別 Class MyClass,並建立一個自定義型別

Yii2分頁的使用及其擴充套件方法

前言: 說明下我們本篇文章都要講哪些內容 分頁的使用,一步一步的教你怎麼做 分頁類LinkPager和Pagination都可以自定義哪些屬性 分頁類LinkPager如何擴充套件成我們所需要的 上下頁按鈕以及10個按鈕 首先,我們把上下頁的按鈕修改成中文 <

C# Process.Start()方法

stat 字符串 檔案 atd 一個 用戶名 菜單 簡單介紹 run System.Diagnostics.Process.Start(); 能做什麽呢?它主要有以下幾個功能: 1、打開某個鏈接網址(彈窗)。 2、定位打開某個文件目錄。 3、打開系統特殊文件夾,如“控制面板

C#基礎 一(方法

命名 可選參數 編譯 標記 .com 操作 改變 根據 ref 需要知道:類和方法的關系 方法和參數修飾符 自定義方法可以有或沒有參數,也可以有或沒有返回值。可以被各種關鍵字(static、virtual、public、new等)修飾以限制其行為。

C# sqlserver ExecuteNonQuery()方法

ransac 行數 itl 有用 進行 返回值 del ace query 關於ExecuteNonQuery() 方法以前對這個一直都沒在意,基本上都沒有用其返回值,查了一下MSDN,如下:SqlCommand.ExecuteNonQuery 方法對連接執行 Transa

關於Quartus構建nios軟核以及eclipse建立c語言工程以及成功下載到FPGA晶片過程遇到的各種問題以及解決方法

這不是一篇構建nios的教程,而是遇到的各種問題以及解決方法。至於構建教程,網上一大把,我推薦正點原子的FPGA教程,比較新,比較詳細,通俗易懂!!! 這裡以一個點亮LED燈的Nios軟核為例,很明顯,需要如下IP核,以及正確的連線(否則各種莫名其妙的錯誤),效果如下所示:

eclipse建立c語言工程以及成功下載到FPGA晶片過程遇到的各種問題以及解決方法

推薦大家預先建立好一個工程目錄資料夾,確實挺好用,參考正點原子的pdf教程,如下圖所示, 我們eclipse在software資料夾建立一個workspace即可 選擇用helloworld模板建立工程,因為這樣可以避免一些問題,比如我遇到的,system.h等標頭檔

阿拉伯數字轉中文數字方法C++實現)

阿拉伯數字與中文數字沒有一一對應關係,不存在直接轉換的公式化演算法,因此需要根據兩種數字體系的特點精心構造轉換演算法。 中文計數有一個特點,就是“零”的使用變化多端。阿拉伯數字中數字的權位依靠數字在整個數字長度中的偏移位置確定,因此數字中間出現的0用於標記數字的偏移位置,即便是連續出現的0也不能省略。中文計

c# 多個相同控制元件使用同一個方法

相信很多朋友遇到多個button執行一個帶著不同引數的方法,或者多個checkbox執行同樣型別的方法,程式碼繁瑣又羅素,都是複製黏貼的活,沒點技術含量,下面就是”充電五分鐘,通話半小時”的寫法。 不囉嗦,上乾貨!  private void cb_Check1_Ch

開啟檔案open()函式的使用方法--C語言函式

標頭檔案:#include <sys/types.h>    #include <sys/stat.h>    #include <fcntl.h>定義函式:    int open(const char * pathname, int

C# DataTable使用方法

  在專案中常常常使用到DataTable,假設DataTable使用得當,不僅能使程式簡潔有用,並且可以提高效能,達到事半功倍的效果,現對DataTable的使用技巧進行一下總結。 1、新增引用 ? 1 using System.D

c++中呼叫Com元件的方法

轉載自:http://www.cppblog.com/woaidongmao/archive/2011/01/10/138250.html需求: 1.建立myCom.dll,該COM只有一個元件,兩個介面:    IGetRes--方法Hello(),    IGetResEx--方法HelloEx() 2

C++11中類資料成員初始化方法

C++98為類中提供類成員的初始化列表。 類物件的構造順序是這樣的:1.分配記憶體,呼叫建構函式時,隱式/顯示的初始化各資料成員 2.進入建構函式後在建構函式中執行一般計算   1.類裡面的任何成員變數在定義時是不能初始化的。   2.一般的資料成員可以在建構函式中初始化。   3.const資料成員必須在

c++ set集合的使用方法

set集合是c++ stl庫中自帶的一個容器,set具有以下兩個特點: 1、set中的元素都是排好序的 2、set集合中沒有重複的元素 常用操作: begin()    返回set容器的第一個元素的地址 end()      返回set容器的最後一個元素地址 clear()

C# Parse and TryParse 方法

工作中遇到的常用方法: Parse and TryParse  TryParse 方法類似於 Parse 方法,不同之處在於 TryParse 方法在轉換失敗時不引發異常 /// <summary> /// TryParse 方法類似於

C#實現匯入匯出Excel資料的兩種方法

這篇文章主要為大家詳細介紹了C#匯入匯出Excel資料的兩種方法,具有一定的參考價值,感興趣的小夥伴們可以參考一下本文為大家分享了C#匯入匯出Excel資料的具體程式碼,供大家參考,具體內容如下注:對於實體類物件最好新建一個並且繼承原有實體類,這樣可以將型別進行修改;方法一:

c++ vector(向量)使用方法

vector 是向量型別,它可以容納許多型別的資料,如若干個整數,所以稱其為容器。vector 是C++ STL的一個重要成員,使用它時需要包含標頭檔案:#include<vector>; vector 容器的長度不固定,能夠在程式執行時動態地改變。 一、v

C++中呼叫ActiveX元件的方法

  本文以 "msscript.ocx" 作為參考   第一步:   獲取標頭檔案   #import "msscript.ocx"   得到兩個檔案 "msscript.tlh" 和 "msscript.tli" 整合下的到個頭檔案"msscript.h"如下:   +