自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

C#擴(kuò)展方法:對(duì)擴(kuò)展進(jìn)行分組管理

開(kāi)發(fā) 后端
本文介紹了c#擴(kuò)展方法的應(yīng)用,即對(duì)擴(kuò)展進(jìn)行分組管理,希望這些內(nèi)容對(duì)大家有所幫助。

從系列文章開(kāi)篇到現(xiàn)在,已經(jīng)實(shí)現(xiàn)的很多擴(kuò)展了,但過(guò)多的擴(kuò)展會(huì)給我們帶來(lái)很多麻煩,試看下圖:

C#擴(kuò)展方法圖1 

C#擴(kuò)展方法圖1

面對(duì)這么多“泛濫”的擴(kuò)展,很多人都會(huì)感到很別扭,的確有種“喧賓奪主”的感覺(jué),想從中找出真正想用的方法來(lái)太難了!盡管經(jīng)過(guò)擴(kuò)展后的string類很“強(qiáng)大”,但易用性確很差。

很多人因此感覺(jué)擴(kuò)展應(yīng)適可而止,不該再繼續(xù)下去...其實(shí)這是一種逃避問(wèn)題的態(tài)度,出現(xiàn)問(wèn)題我們應(yīng)該主動(dòng)去解決,而不是去回避!

有很多種方法可以解決以上問(wèn)題,最簡(jiǎn)單的就是使用將擴(kuò)展放入不同namespace中,使用時(shí)按需using相應(yīng)namespace,可達(dá)到一定效果。但這種方法有很大缺點(diǎn): 一個(gè)命名空間中的擴(kuò)展若太多同樣會(huì)讓我們的智能提示充斥著擴(kuò)展方法,擴(kuò)展太少每次使用都要using多個(gè)命名空間,很麻煩。

 先介紹一種簡(jiǎn)單的方式,先看效果:

C#擴(kuò)展方法圖2 

C#擴(kuò)展方法圖2

圖1中前三個(gè)以As開(kāi)始的三個(gè)擴(kuò)展就是采用分組技術(shù)后的三類擴(kuò)展,分別是中文處理、轉(zhuǎn)換操作、正則操作,后面三個(gè)圖分別對(duì)就這三類擴(kuò)展的具體應(yīng)用。圖2中的有三個(gè)中文處理的擴(kuò)展ToDBC、ToSBC、GetChineseSpell分別是轉(zhuǎn)為半角、轉(zhuǎn)為全角、獲取拼音首字母。

 通過(guò)這樣分組后,string類的智能提示中擴(kuò)展泛濫的現(xiàn)象得到了解決,使用AsXXX,是以字母A開(kāi)始,會(huì)出現(xiàn)在提示的最前面,與原生方法區(qū)分開(kāi)來(lái)。

 采用這種方式有幾個(gè)缺點(diǎn):

 1.使用一個(gè)擴(kuò)展要先As一次,再使用具體擴(kuò)展,比之前多了一步操作:這是分組管理必然的,建議使用頻率非常高的還是直接擴(kuò)展給string類,不要分組。只對(duì)使用頻率不高的進(jìn)行分組。

 2.擴(kuò)展后的智能提示不友好,擴(kuò)展的方法與Equals、ToString混在了一起,而且沒(méi)有擴(kuò)展方法的標(biāo)志。

 先給出這種方法的實(shí)現(xiàn)參考代碼,再來(lái)改進(jìn): 

  1. public static class StringExtension  
  2.  {  
  3.      public static ChineseString AsChineseString(this string s) { return new ChineseString(s); }  
  4.      public static ConvertableString AsConvertableString(this string s) { return new ConvertableString(s); }  
  5.      public static RegexableString AsRegexableString(this string s) { return new RegexableString(s); }  
  6.  }  
  7.  public class ChineseString  
  8.  {  
  9.      private string s;  
  10.      public ChineseString(string s) { this.s = s; }  
  11.      //轉(zhuǎn)全角  
  12.      public string ToSBC(string input) { throw new NotImplementedException(); }   
  13.      //轉(zhuǎn)半角  
  14.      public string ToDBC(string input) { throw new NotImplementedException(); }  
  15.      //獲取漢字拼音首字母  
  16.      public string GetChineseSpell(string input) { throw new NotImplementedException(); }  
  17.  }  
  18.  public class ConvertableString  
  19.  {  
  20.      private string s;  
  21.      public ConvertableString(string s) { this.s = s; }  
  22.      public bool IsInt(string s) { throw new NotImplementedException(); }  
  23.      public bool IsDateTime(string s) { throw new NotImplementedException(); }  
  24.      public int ToInt(string s) { throw new NotImplementedException(); }  
  25.      public DateTime ToDateTime(string s) { throw new NotImplementedException(); }   
  26.  }  
  27.  public class RegexableString  
  28.  {  
  29.      private string s;  
  30.      public RegexableString(string s) { this.s = s; }  
  31.      public bool IsMatch(string s, string pattern) { throw new NotImplementedException(); }  
  32.      public string Match(string s, string pattern) { throw new NotImplementedException(); }  
  33.      public string Relplace(string s, string pattern, MatchEvaluator evaluator) { throw new NotImplementedException(); }  
  34.  } 

 代碼僅是為了說(shuō)明怎么分組,沒(méi)有實(shí)現(xiàn),具體實(shí)現(xiàn)請(qǐng)參見(jiàn)本系列前面的文章。為了節(jié)省空間,很多代碼都寫(xiě)成了一行。

 前面提到的第二條缺點(diǎn),我們改進(jìn)后,方式二的顯示效果如下:

C#擴(kuò)展方法圖3 

C#擴(kuò)展方法圖3

 Equals、GetHashCode、ToString 實(shí)在去不了,哪位朋友有好辦法分享一下吧!不過(guò)這次把擴(kuò)展方法的標(biāo)志加上。實(shí)現(xiàn)比方式一麻煩一下:

  1. public class ChineseString  
  2. {  
  3.     private string s;  
  4.     public ChineseString(string s) { this.s = s; }  
  5.     public string GetValue() { return s; }  
  6. }  
  7.  
  8. public static class CheseStringExtension  
  9. {  
  10.     public static ChineseString AsChineseString(this string s) { return new ChineseString(s); }  
  11.  
  12.     public static string ToSBC(this ChineseString cs)   
  13.     {  
  14.         string s = cs.GetValue();//從ChineseString取出原string  
  15.         char[] c = s.ToCharArray();  
  16.         for (int i = 0; i <  c.Length; i++)  
  17.         {  
  18.             if (c[i] == 32) { c[i] = (char)12288; continue; }                  
  19.             if (c[i] <  127) c[i] = (char)(c[i] + 65248);  
  20.         }  
  21.         return new string(c);  
  22.     }  
  23.     public static string ToDBC(this ChineseString cs) { throw new NotImplementedException(); }  
  24.     public static string GetChineseSpell(this ChineseString cs) { throw new NotImplementedException(); }  

這里需要兩個(gè)類,一個(gè)類ChineseString作為AsXXX的返回值,第二個(gè)類ChineseStringExtension是對(duì)ChineseString進(jìn)行擴(kuò)展的類。能過(guò)這種方式,才能顯示出擴(kuò)展的標(biāo)識(shí)符號(hào)!每組擴(kuò)展要兩個(gè)類,比較麻煩。

方式一、方式二感覺(jué)都不太好,而且擴(kuò)展組多了,還會(huì)有新的問(wèn)題出現(xiàn),如下:

C#擴(kuò)展方法圖4 

C#擴(kuò)展方法圖4

也是很要命的!再來(lái)看第三種方式,這是我和韋恩卑鄙在討論單一職責(zé)原則時(shí)想出來(lái)的,先看效果:

C#擴(kuò)展方法圖5 

C#擴(kuò)展方法圖5

 方法三將所有的擴(kuò)展精簡(jiǎn)為一個(gè)As< T>!是的,我們僅需要As< T>這一個(gè)擴(kuò)展!T為一接口,通過(guò)輸入不同的T,展示相應(yīng)的擴(kuò)展。這樣又解決了擴(kuò)展組的泛濫問(wèn)題,先看下實(shí)現(xiàn)一個(gè)新的擴(kuò)展組需要寫(xiě)什么代碼,先看左圖的代碼:

  1. public interface IConvertableString : IExtension< string> { }  
  2.  
  3. public static class ConvertableString  
  4. {  
  5.     public static bool IsInt(this IConvertableString s)  
  6.     {  
  7.         int i; return int.TryParse(s.GetValue(), out i);  
  8.     }  
  9.     public static bool IsDateTime(this IConvertableString s)  
  10.     {  
  11.         DateTime d; return DateTime.TryParse(s.GetValue(), out d);  
  12.     }  
  13.  
  14.     public static int ToInt(this IConvertableString s)  
  15.     {  
  16.         return int.Parse(s.GetValue());  
  17.     }  
  18.  
  19.     public static DateTime ToDateTime(this IConvertableString s)  
  20.     {  
  21.        return DateTime.Parse(s.GetValue());  
  22.     }  

首先定義一個(gè)接口IConvertableString,它繼承泛型接口IExtension< T>(我定義的一個(gè)接口,稍后給出),因?yàn)槭菍?duì)string類作擴(kuò)展,所以泛型參數(shù)為string。IConvertableString只需要一個(gè)空架子。然后再編寫(xiě)一個(gè)擴(kuò)展類,所有的方法擴(kuò)展在IConvertableString接口上。

再來(lái)看右圖IRegexableString的代碼:

  1. public static class RegexableString  
  2. {  
  3.     public static bool IsMatch(this IRegexableString s, string pattern)  
  4.     { throw new NotImplementedException(); }  
  5.     public static string Match(this IRegexableString s, string pattern)  
  6.     { throw new NotImplementedException(); }  
  7.     public static string Relplace(this IRegexableString s, string pattern, MatchEvaluator evaluator)  
  8.     { throw new NotImplementedException(); }  

與上一個(gè)一樣,也是先定義一個(gè)空接口,再定義一個(gè)擴(kuò)展類,將方法擴(kuò)展在空接口上。

 有一點(diǎn)注意一下,擴(kuò)展的實(shí)現(xiàn)中都要使用GetValue獲取原始字符串的值。

 ***給出IExtension< T>接口及As< T>擴(kuò)展的實(shí)現(xiàn): 

  1. public interface IExtension< V>  
  2. {  
  3.     V GetValue();  
  4. }  
  5.  
  6. public static class ExtensionGroup  
  7. {  
  8.     private static Dictionary< Type, Type> cache = new Dictionary< Type, Type>();  
  9.  
  10.     public static T As< T>(this string v) where T : IExtension< string>  
  11.     {  
  12.         return As< T, string>(v);  
  13.     }  
  14.  
  15.     public static T As< T, V>(this V v) where T : IExtension< V>  
  16.     {  
  17.         Type t;  
  18.         Type valueType = typeof(V);  
  19.         if (cache.ContainsKey(valueType))  
  20.         {  
  21.             t = cache[valueType];  
  22.         }  
  23.         else 
  24.         {  
  25.             t = CreateType< T, V>();  
  26.             cache.Add(valueType, t);  
  27.         }  
  28.         object result = Activator.CreateInstance(t, v);  
  29.         return (T)result;  
  30.     }  
  31.     // 通過(guò)反射發(fā)出動(dòng)態(tài)實(shí)現(xiàn)接口T  
  32.     private static Type CreateType< T, V>() where T : IExtension< V>  
  33.     {  
  34.         Type targetInterfaceType = typeof(T);  
  35.         string generatedClassName = targetInterfaceType.Name.Remove(0, 1);  
  36.         //  
  37.         AssemblyName aName = new AssemblyName("ExtensionDynamicAssembly");  
  38.         AssemblyBuilder ab =  
  39.             AppDomain.CurrentDomain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Run);  
  40.         ModuleBuilder mb = ab.DefineDynamicModule(aName.Name);  
  41.         TypeBuilder tb = mb.DefineType(generatedClassName, TypeAttributes.Public);  
  42.         //實(shí)現(xiàn)接口  
  43.         tb.AddInterfaceImplementation(typeof(T));  
  44.         //value字段  
  45.         FieldBuilder valueFiled = tb.DefineField("value"typeof(V), FieldAttributes.Private);  
  46.         //構(gòu)造函數(shù)  
  47.         ConstructorBuilder ctor = tb.DefineConstructor(MethodAttributes.Public,  
  48.             CallingConventions.Standard, new Type[] { typeof(V) });  
  49.         ILGenerator ctor1IL = ctor.GetILGenerator();  
  50.         ctor1IL.Emit(OpCodes.Ldarg_0);  
  51.         ctor1IL.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));  
  52.         ctor1IL.Emit(OpCodes.Ldarg_0);  
  53.         ctor1IL.Emit(OpCodes.Ldarg_1);  
  54.         ctor1IL.Emit(OpCodes.Stfld, valueFiled);  
  55.         ctor1IL.Emit(OpCodes.Ret);  
  56.         //GetValue方法  
  57.         MethodBuilder getValueMethod = tb.DefineMethod("GetValue",  
  58.             MethodAttributes.Public | MethodAttributes.Virtual, typeof(V), Type.EmptyTypes);  
  59.         ILGenerator numberGetIL = getValueMethod.GetILGenerator();  
  60.         numberGetIL.Emit(OpCodes.Ldarg_0);  
  61.         numberGetIL.Emit(OpCodes.Ldfld, valueFiled);  
  62.         numberGetIL.Emit(OpCodes.Ret);  
  63.         //接口實(shí)現(xiàn)  
  64.         MethodInfo getValueInfo = targetInterfaceType.GetInterfaces()[0].GetMethod("GetValue");  
  65.         tb.DefineMethodOverride(getValueMethod, getValueInfo);  
  66.         //  
  67.         Type t = tb.CreateType();  
  68.         return t;  
  69.     }  

代碼比較長(zhǎng),先折疊起來(lái),逐層打開(kāi)分析吧!

IExtension< V>只定義一個(gè)方法GetValue,用于將As< T>后將原始的值取出。

ExtensionGroup定義了As< T>擴(kuò)展,我們先看下值的傳遞過(guò)程。調(diào)用語(yǔ)句:"123".As< IConvertableString>().ToInt();

首先,"123" 是個(gè)字符串,As< IConvertableString>后轉(zhuǎn)換成了IConvertableString接口的實(shí)例,ToInt時(shí)使用GetValue將"123"從IConvertableString接口的實(shí)例中取出進(jìn)行處理。

關(guān)鍵在“IConvertableString接口的實(shí)例”,前面我們并沒(méi)有具體實(shí)現(xiàn)IConvertableString接口的類,怎么出來(lái)的實(shí)例呢?我們這里用反射發(fā)出動(dòng)態(tài)生成了一個(gè)實(shí)現(xiàn)IConvertableString接口的類。具體是由ExtensionGroup中的私有函數(shù)CreateType< T, V>完成的,在這里T傳入的是IConvertableString,V傳入的是string,返回的值就是實(shí)現(xiàn)了IConvertableString接口的一個(gè)類的Type.由CreateType< T, V>動(dòng)態(tài)實(shí)現(xiàn)的類“模樣”如下:

  1. class ConvertableString : IConvertableString  
  2. {  
  3.     private string value;  
  4.     public ConvertableString(string value)  
  5.     {  
  6.             this.value = value;  
  7.     }  
  8.     public string GetValue()  
  9.     {  
  10.        return value;  
  11.    }  

如果此處不用反射發(fā)出動(dòng)態(tài)生成這么一個(gè),那么我們就要手工寫(xiě)一個(gè),每個(gè)擴(kuò)展組都要相應(yīng)的寫(xiě)一個(gè),很麻煩的。

為了提高性能,對(duì)反射發(fā)出的類型進(jìn)行了緩存,保存在cache成員中。

方式三有點(diǎn)復(fù)雜,主要是因?yàn)槲覀兪墙osealed類進(jìn)行擴(kuò)展,無(wú)法從它們繼承。

***給出測(cè)試代碼:

  1. public static void Test()  
  2. {  
  3.     int i = "123".As< IConvertableString>().ToInt();  
  4.     DateTime d = "2009年8月29日".As< IConvertableString>().ToDateTime();  

三種方式,我最喜歡第三種,它僅需要一個(gè)As< T>,而且是對(duì)接口進(jìn)行擴(kuò)展,感覺(jué)更OO一些。

三種方式都不***,我會(huì)努力改進(jìn),大家多提些建議啊。

【編輯推薦】

  1. 淺析C#啟動(dòng)停止SQL數(shù)據(jù)庫(kù)服務(wù)之方法
  2. 總結(jié)C#獲取當(dāng)前路徑的7種方法
  3. 淺析C# treeview控件的使用方法
  4. C#多態(tài)性的概念及其應(yīng)用
  5. 介紹C#構(gòu)造函數(shù)的使用方法

 

責(zé)任編輯:book05 來(lái)源: cnblogs
相關(guān)推薦

2009-08-27 18:04:01

c#擴(kuò)展方法string

2009-08-10 17:36:17

C#擴(kuò)展方法

2009-09-01 11:04:59

C#調(diào)用擴(kuò)展方法

2009-04-03 13:20:05

C#擴(kuò)展方法調(diào)用

2009-08-18 14:14:45

C#擴(kuò)展方法性能測(cè)試

2009-08-27 09:27:49

C#擴(kuò)展方法

2009-08-26 15:53:48

C#擴(kuò)展方法

2009-08-27 16:24:48

擴(kuò)展方法C# 3.0新特性

2023-12-12 07:30:54

IstioWasm前端

2009-08-13 17:06:37

C#擴(kuò)展方法Enumerable.

2009-09-01 11:19:47

C# 3.0擴(kuò)展重載抉

2021-05-06 09:33:32

OperatorKubernetes開(kāi)源

2009-08-28 14:25:57

C# byte數(shù)組

2009-09-02 17:08:30

C#語(yǔ)言開(kāi)發(fā)Windo

2009-09-03 17:33:08

C#常規(guī)擴(kuò)展性模型

2009-09-03 17:18:40

C#擴(kuò)展性對(duì)象模型

2011-08-04 10:57:33

Objective-C C語(yǔ)言 BOOL

2009-09-03 13:50:22

ToString(st

2009-04-03 10:25:32

C#XML擴(kuò)展代碼

2024-05-30 07:29:59

開(kāi)源工具類庫(kù)擴(kuò)展
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)