溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

C#如何對擴(kuò)展進(jìn)行分組管理

發(fā)布時(shí)間:2021-07-16 10:35:32 來源:億速云 閱讀:172 作者:chen 欄目:編程語言

本篇內(nèi)容主要講解“C#如何對擴(kuò)展進(jìn)行分組管理”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“C#如何對擴(kuò)展進(jìn)行分組管理”吧!

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

C#如何對擴(kuò)展進(jìn)行分組管理

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

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

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

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

先介紹一種簡單的方式,先看效果:

C#如何對擴(kuò)展進(jìn)行分組管理

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

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

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

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

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

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

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

public static class StringExtension   {       public static ChineseString AsChineseString(this string s) { return new ChineseString(s); }       public static ConvertableString AsConvertableString(this string s) { return new ConvertableString(s); }       public static RegexableString AsRegexableString(this string s) { return new RegexableString(s); }   }   public class ChineseString   {       private string s;       public ChineseString(string s) { this.s = s; }       //轉(zhuǎn)全角       public string ToSBC(string input) { throw new NotImplementedException(); }        //轉(zhuǎn)半角       public string ToDBC(string input) { throw new NotImplementedException(); }       //獲取漢字拼音首字母       public string GetChineseSpell(string input) { throw new NotImplementedException(); }   }   public class ConvertableString   {       private string s;       public ConvertableString(string s) { this.s = s; }       public bool IsInt(string s) { throw new NotImplementedException(); }       public bool IsDateTime(string s) { throw new NotImplementedException(); }       public int ToInt(string s) { throw new NotImplementedException(); }       public DateTime ToDateTime(string s) { throw new NotImplementedException(); }    }   public class RegexableString   {       private string s;       public RegexableString(string s) { this.s = s; }       public bool IsMatch(string s, string pattern) { throw new NotImplementedException(); }       public string Match(string s, string pattern) { throw new NotImplementedException(); }       public string Relplace(string s, string pattern, MatchEvaluator evaluator) { throw new NotImplementedException(); }   }

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

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

C#如何對擴(kuò)展進(jìn)行分組管理

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

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

public class ChineseString  {      private string s;      public ChineseString(string s) { this.s = s; }      public string GetValue() { return s; }  }   public static class CheseStringExtension  {      public static ChineseString AsChineseString(this string s) { return new ChineseString(s); }       public static string ToSBC(this ChineseString cs)       {          string s = cs.GetValue();//從ChineseString取出原string          char[] c = s.ToCharArray();          for (int i = 0; i <  c.Length; i++)          {              if (c[i] == 32) { c[i] = (char)12288; continue; }                              if (c[i] <  127) c[i] = (char)(c[i] + 65248);          }          return new string(c);      }      public static string ToDBC(this ChineseString cs) { throw new NotImplementedException(); }      public static string GetChineseSpell(this ChineseString cs) { throw new NotImplementedException(); }  }

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

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

C#如何對擴(kuò)展進(jìn)行分組管理

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

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

C#如何對擴(kuò)展進(jìn)行分組管理

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

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

public interface IConvertableString : IExtension< string> { }   public static class ConvertableString  {      public static bool IsInt(this IConvertableString s)      {          int i; return int.TryParse(s.GetValue(), out i);      }      public static bool IsDateTime(this IConvertableString s)      {          DateTime d; return DateTime.TryParse(s.GetValue(), out d);      }       public static int ToInt(this IConvertableString s)      {          return int.Parse(s.GetValue());      }       public static DateTime ToDateTime(this IConvertableString s)      {         return DateTime.Parse(s.GetValue());      }  }

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

再來看右圖IRegexableString的代碼:

public static class RegexableString  {      public static bool IsMatch(this IRegexableString s, string pattern)      { throw new NotImplementedException(); }      public static string Match(this IRegexableString s, string pattern)      { throw new NotImplementedException(); }      public static string Relplace(this IRegexableString s, string pattern, MatchEvaluator evaluator)      { throw new NotImplementedException(); }  }

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

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

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

public interface IExtension< V>  {      V GetValue();  }   public static class ExtensionGroup  {      private static Dictionary< Type, Type> cache = new Dictionary< Type, Type>();       public static T As< T>(this string v) where T : IExtension< string>      {          return As< T, string>(v);      }       public static T As< T, V>(this V v) where T : IExtension< V>      {          Type t;          Type valueType = typeof(V);          if (cache.ContainsKey(valueType))          {              t = cache[valueType];          }          else         {              t = CreateType< T, V>();              cache.Add(valueType, t);          }          object result = Activator.CreateInstance(t, v);          return (T)result;      }      // 通過反射發(fā)出動態(tài)實(shí)現(xiàn)接口T      private static Type CreateType< T, V>() where T : IExtension< V>      {          Type targetInterfaceType = typeof(T);          string generatedClassName = targetInterfaceType.Name.Remove(0, 1);          //          AssemblyName aName = new AssemblyName("ExtensionDynamicAssembly");          AssemblyBuilder ab =              AppDomain.CurrentDomain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Run);          ModuleBuilder mb = ab.DefineDynamicModule(aName.Name);          TypeBuilder tb = mb.DefineType(generatedClassName, TypeAttributes.Public);          //實(shí)現(xiàn)接口          tb.AddInterfaceImplementation(typeof(T));          //value字段          FieldBuilder valueFiled = tb.DefineField("value", typeof(V), FieldAttributes.Private);          //構(gòu)造函數(shù)          ConstructorBuilder ctor = tb.DefineConstructor(MethodAttributes.Public,              CallingConventions.Standard, new Type[] { typeof(V) });          ILGenerator ctor1IL = ctor.GetILGenerator();          ctor1IL.Emit(OpCodes.Ldarg_0);          ctor1IL.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));          ctor1IL.Emit(OpCodes.Ldarg_0);          ctor1IL.Emit(OpCodes.Ldarg_1);          ctor1IL.Emit(OpCodes.Stfld, valueFiled);          ctor1IL.Emit(OpCodes.Ret);          //GetValue方法          MethodBuilder getValueMethod = tb.DefineMethod("GetValue",              MethodAttributes.Public | MethodAttributes.Virtual, typeof(V), Type.EmptyTypes);          ILGenerator numberGetIL = getValueMethod.GetILGenerator();          numberGetIL.Emit(OpCodes.Ldarg_0);          numberGetIL.Emit(OpCodes.Ldfld, valueFiled);          numberGetIL.Emit(OpCodes.Ret);          //接口實(shí)現(xiàn)          MethodInfo getValueInfo = targetInterfaceType.GetInterfaces()[0].GetMethod("GetValue");          tb.DefineMethodOverride(getValueMethod, getValueInfo);          //          Type t = tb.CreateType();          return t;      }  }

代碼比較長,先折疊起來,逐層打開分析吧!

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

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

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

關(guān)鍵在“IConvertableString接口的實(shí)例”,前面我們并沒有具體實(shí)現(xiàn)IConvertableString接口的類,怎么出來的實(shí)例呢?我們這里用反射發(fā)出動態(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>動態(tài)實(shí)現(xiàn)的類“模樣”如下:

class ConvertableString : IConvertableString  {      private string value;      public ConvertableString(string value)      {              this.value = value;      }      public string GetValue()      {         return value;     }

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

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

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

***給出測試代碼:

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

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

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

到此,相信大家對“C#如何對擴(kuò)展進(jìn)行分組管理”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI