代碼越寫越多,但是我們也需要經常去反思那些寫過的代碼,Utility Class就是這一類需要特別去反思總結的類,這些類像工具一樣,我們經常通過一些靜態方法,通過傳入一些參數,然后得到我們需要的結果,在下面的一系列博客中,我們就一點一滴去反思這些類,並提供一個很好的方式去為他人提供方便,促進彼此之間的相互學習。
首先第一個介紹的就是ParseUtil這個靜態類,在我們的代碼中,我們經常需要去將string類型的字符串去做一個類型轉換,比如說int,double,demical,float,DateTime、short、long甚至是byte數組類型,當我們轉換失敗時還可以提供一個默認值,這些值能夠保證一定去返回這些正確的類型值。
這組代碼的核心部分就是:
private static T ParseStringToType<T>(this string input, Func<string, T> action, T defaultvalue) where T : struct { if (string.IsNullOrEmpty(input)) { return defaultvalue; } try { return action(input); } catch { return defaultvalue; } }
這段代碼的核心部分就是通過傳入一個Func<string, T>類型的委托去構建代碼和算法的重用,這里涉及到一些重要的思想,一個是代碼重用思想,另外就是體會到使用泛型參數T來真正地實現算法的重用機制,另外通過這個我們也可以橫向比較Action、Func類型的委托以及匿名委托delegate的一些區別。
public static class ParseUtil { public static DateTime ParseByDefault(this string input, DateTime defaultvalue) { return input.ParseStringToType<DateTime>(delegate(string e) { return Convert.ToDateTime(input); }, defaultvalue); } public static decimal ParseByDefault(this string input, decimal defaultvalue) { return input.ParseStringToType<decimal>(delegate(string e) { return Convert.ToDecimal(input); }, defaultvalue); } public static double ParseByDefault(this string input, double defaultvalue) { return input.ParseStringToType<double>(delegate(string e) { return Convert.ToDouble(input); }, defaultvalue); } public static int ParseByDefault(this string input, int defaultvalue) { return input.ParseStringToType<int>(delegate(string e) { return Convert.ToInt32(input); }, defaultvalue); } public static long ParseByDefault(this string input, long defaultvalue) { return input.ParseStringToType<long>(delegate(string e) { return Convert.ToInt64(input); }, defaultvalue); } public static float ParseByDefault(this string input, float defaultvalue) { return input.ParseStringToType<float>(delegate(string e) { return Convert.ToSingle(input); }, defaultvalue); } public static float ParseByDefault(this string input, short defaultvalue) { return input.ParseStringToType<short>(delegate(string e) { return Convert.ToInt16(input); }, defaultvalue); } public static string ParseByDefault(this string input, string defaultvalue) { if (string.IsNullOrEmpty(input)) { return defaultvalue; } return input; } private static T ParseStringToType<T>(this string input, Func<string, T> action, T defaultvalue) where T : struct { if (string.IsNullOrEmpty(input)) { return defaultvalue; } try { return action(input); } catch { return defaultvalue; } } }
另外在傳入參數input之前為什么還需要傳遞this?這個該如何去准確理解呢?
這就涉及到C# this擴展方法的內容了......
擴展方法被定義為靜態方法,但它們是通過實例方法語法進行調用的。 它們的第一個參數指定該方法作用於哪個類型,並且該參數以 this 修飾符為前綴。 擴展方法當然不能破壞面向對象封裝的概念,所以只能是訪問所擴展類的public成員。
擴展方法使您能夠向現有類型“添加”方法,而無需創建新的派生類型、重新編譯或以其他方式修改原始類型。擴展方法是一種特殊的靜態方法,但可以像擴展類型上的實例方法一樣進行調用,C#擴展方法第一個參數指定該方法作用於哪個類型,並且該參數以 this 修飾符為前綴。
就像上面的方法中,input是一個string類型的對象,但是 ParseStringToType這個方法是我們自己定義的一個靜態方法,String類中是不存在該方法的,是我們人為去擴展的一個方法,下面舉出一個相同的例子:
//必須是靜態類才可以添加擴展方法
Static class Program
{
static void Main(string[] args)
{
string str = "quzijing";
//注意調用擴展方法,必須用對象來調用
string Newstr = str.Add();
Console.WriteLine(Newstr);
Console.ReadKey();
}
//聲明擴展方法
//擴展方法必須是靜態的,Add有三個參數
//this 必須有,string表示我要擴展的類型,stringName表示對象名
//三個參數this和擴展的類型必不可少,對象名可以自己隨意取如果需要傳遞參數,//再增加一個變量即可
public static string Add(this string stringName)
{
return stringName+"a";
}
}
既然string類可以通過這種方式來擴展方法,那么我們定義的一般類呢?答案也是可以的。
給自定義的類型增加一個擴展方法,並增加一個傳遞的參數
(1)、聲明一個Student類,它包含了兩個方法StuInfo,getStuInfo
public class Student
{
public string StuInfo()
{
return "學生基本信息";
}
public string getStuInfo(string stuName, string stuNum)
{
return string.Format("學生信息:\\n" + "姓名:{0} \\n" + "學號:{1}", stuName, stuNum);
}
}
(2)、聲明一個名為ExtensionStudentInfo的靜態類,注意必須為靜態
這個類的作用就是包含一些我們想要擴展的方法,在此我們聲明兩個Student類型的擴展方法,Student類型為我們自定義的類型。
public static class ExtensionStudentInfo
{
//聲明擴展方法
//要擴展的方法必須是靜態的方法,Add有三個參數
//this 必須有,string表示我要擴展的類型,stringName表示對象名
//三個參數this和擴展的類型必不可少,對象名可以自己隨意取如果需要傳遞參數,再增加一個變量即可
public static string ExtensionStuInfo(this Student stuName)
{
return stuName.StuInfo();
}
//聲明擴展方法
//要擴展的方法必須是靜態的方法,Add有三個參數
//this 必須有,string表示我要擴展的類型,stringName表示對象名
//三個參數this和擴展的類型必不可少,對象名可以自己隨意取如果需要傳遞參數,在此我們增加了兩個string類型的參數
public static string ExtensionGetStuInfo(this Student student, string stuname, string stunum)
{
return student.getStuInfo(stuname, stunum)+"\\n讀取完畢";
}
}
下面來建一個測試的應用程序來看看輸出的結果
static void Main(string[] args)
{
Student newstudent = new Student();
//要使用對象調用我們的擴展方法
string stuinfo = newstudent.ExtensionStuInfo();
Console.WriteLine(stuinfo);
//要使用對象調用我們的擴展方法
string stuinformation = newstudent.ExtensionGetStuInfo("張三", "001");
Console.WriteLine(stuinformation);
Console.ReadKey();
}
獲取的結果是:
最后借用網上的一個例子來結束這一內容的介紹。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; //聲明擴展方法的步驟:類必須是static,方法是static, //第一個參數是被擴展的對象,前面標注this。 //使用擴展方法的時候必須保證擴展方法類已經在當前代碼中using namespace 擴展方法 { //擴展方法必須是靜態的 public static class StringHelper { //擴展方法必須是靜態的,第一個參數必須加上this public static bool IsEmail(this string _input) { return Regex.IsMatch(_input, @"^\\w+@\\w+\\.\\w+$"); } //帶多個參數的擴展方法 //在原始字符串前后加上指定的字符 public static string Quot(this string _input, string _quot) { return _quot + _input + _quot; } } }
通過擴展類,我們可以為string類擴展一些原先不太需要的方法來完成我們的需求。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace 擴展方法 { class Program { static void Main(string[] args) { string _myEmail = "abc@163.com"; //這里就可以直接使用string類的擴展方法IsEmail了 Console.WriteLine(_myEmail.IsEmail()); //調用接收參數的擴展方法 Console.WriteLine(_myEmail.Quot("!")); Console.ReadLine(); } } }
就像我們的ParseUtil類定義的那樣,我們可以通過定義string str,然后通過str.ParseByDefault(0D)直接將我們將string直接轉化為double類型,而不用在每個地方都重復去寫代碼,這些都需要我們不斷去反思總結代碼,代碼才能越寫越好;