c#扩展方法奇思妙用高级篇四:对扩展进行分组管理
?
?面對這么多“泛濫”的擴展,很多人都會感到很別扭,的確有種“喧賓奪主”的感覺,想從中找出真正想用的方法來太難了!盡管經過擴展后的string類很“強大”,但易用性確很差。
?很多人因此感覺擴展應適可而止,不該再繼續下去...其實這是一種逃避問題的態度,出現問題我們應該主動去解決,而不是去回避!
?有很多種方法可以解決以上問題,最簡單的就是使用將擴展放入不同namespace中,使用時按需using相應namespace,可達到一定效果。但這種方法有很大缺點:?一個命名空間中的擴展若太多同樣會讓我們的智能提示充斥著擴展方法,擴展太少每次使用都要using多個命名空間,很麻煩。
?先介紹一種簡單的方式,先看效果:
?
?圖1中前三個以As開始的三個擴展就是采用分組技術后的三類擴展,分別是中文處理、轉換操作、正則操作,后面三個圖分別對就這三類擴展的具體應用。圖2中的有三個中文處理的擴展ToDBC、ToSBC、GetChineseSpell分別是轉為半角、轉為全角、獲取拼音首字母。
?通過這樣分組后,string類的智能提示中擴展泛濫的現象得到了解決,使用AsXXX,是以字母A開始,會出現在提示的最前面,與原生方法區分開來。
?采用這種方式有幾個缺點:
?1.使用一個擴展要先As一次,再使用具體擴展,比之前多了一步操作:這是分組管理必然的,建議使用頻率非常高的還是直接擴展給string類,不要分組。只對使用頻率不高的進行分組。
?2.擴展后的智能提示不友好,擴展的方法與Equals、ToString混在了一起,而且沒有擴展方法的標志。
?先給出這種方法的實現參考代碼,再來改進:
?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?????????//轉全角
12?????????public?string?ToSBC(string?input)?{?throw?new?NotImplementedException();?}?
13?????????//轉半角
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?????}
?代碼僅是為了說明怎么分組,沒有實現,具體實現請參見本系列前面的文章。為了節省空間,很多代碼都寫成了一行。
?前面提到的第二條缺點,我們改進后,方式二的顯示效果如下:
?
?Equals、GetHashCode、ToString 實在去不了,哪位朋友有好辦法分享一下吧!不過這次把擴展方法的標志加上。實現比方式一麻煩一下:
?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();?}
25?????}
?這里需要兩個類,一個類ChineseString作為AsXXX的返回值,第二個類ChineseStringExtension是對ChineseString進行擴展的類。能過這種方式,才能顯示出擴展的標識符號!每組擴展要兩個類,比較麻煩。
?方式一、方式二感覺都不太好,而且擴展組多了,還會有新的問題出現,如下:
?
?也是很要命的!再來看第三種方式,這是我和韋恩卑鄙在討論單一職責原則時想出來的,先看效果:
?
?
?方法三將所有的擴展精簡為一個As<T>!是的,我們僅需要As<T>這一個擴展!T為一接口,通過輸入不同的T,展示相應的擴展。這樣又解決了擴展組的泛濫問題,先看下實現一個新的擴展組需要寫什么代碼,先看左圖的代碼:
?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?????????}
23?????}
?首先定義一個接口IConvertableString,它繼承泛型接口IExtension<T>(我定義的一個接口,稍后給出),因為是對string類作擴展,所以泛型參數為string。IConvertableString只需要一個空架子。然后再編寫一個擴展類,所有的方法擴展在IConvertableString接口上。
?再來看右圖IRegexableString的代碼:?
1?????public?static?class?RegexableString2?????{
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();?}
9?????}
?與上一個一樣,也是先定義一個空接口,再定義一個擴展類,將方法擴展在空接口上。
?有一點注意一下,擴展的實現中都要使用GetValue獲取原始字符串的值。
?最后給出IExtension<T>接口及As<T>擴展的實現:??
Code?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????????//?通過反射發出動態實現接口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????????????//實現接口
43????????????tb.AddInterfaceImplementation(typeof(T));
44????????????//value字段
45????????????FieldBuilder?valueFiled?=?tb.DefineField("value",?typeof(V),?FieldAttributes.Private);
46????????????//構造函數
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????????????//接口實現
64????????????MethodInfo?getValueInfo?=?targetInterfaceType.GetInterfaces()[0].GetMethod("GetValue");
65????????????tb.DefineMethodOverride(getValueMethod,?getValueInfo);
66????????????//
67????????????Type?t?=?tb.CreateType();
68????????????return?t;
69????????}
70????}
?代碼比較長,先折疊起來,逐層打開分析吧!
?IExtension<V>只定義一個方法GetValue,用于將As<T>后將原始的值取出。
?ExtensionGroup定義了As<T>擴展,我們先看下值的傳遞過程。調用語句:"123".As<IConvertableString>().ToInt();
?首先,"123" 是個字符串,As<IConvertableString>后轉換成了IConvertableString接口的實例,ToInt時使用GetValue將"123"從IConvertableString接口的實例中取出進行處理。
?關鍵在“IConvertableString接口的實例”,前面我們并沒有具體實現IConvertableString接口的類,怎么出來的實例呢?我們這里用反射發出動態生成了一個實現IConvertableString接口的類。具體是由ExtensionGroup中的私有函數CreateType<T, V>完成的,在這里T傳入的是IConvertableString,V傳入的是string,返回的值就是實現了IConvertableString接口的一個類的Type.由CreateType<T, V>動態實現的類“模樣”如下:
?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?????????}
12?????}
?如果此處不用反射發出動態生成這么一個,那么我們就要手工寫一個,每個擴展組都要相應的寫一個,很麻煩的。
?為了提高性能,對反射發出的類型進行了緩存,保存在cache成員中。
?方式三有點復雜,主要是因為我們是給sealed類進行擴展,無法從它們繼承。
?最后給出測試代碼:?
1?????public?static?void?Test()2?????{
3?????????int?i?=?"123".As<IConvertableString>().ToInt();
4?????????DateTime?d?=?"2009年8月29日".As<IConvertableString>().ToDateTime();
5?????}
??
?三種方式,我最喜歡第三種,它僅需要一個As<T>,而且是對接口進行擴展,感覺更OO一些。
?三種方式都不完美,我會努力改進,大家多提些建議啊。
?最后,謝謝大家對我的運行,特別感謝韋恩卑鄙。
?本人系列文章《c#擴展方法奇思妙用》,敬請關注!??
轉載于:https://www.cnblogs.com/China-Dragon/archive/2010/05/12/1733482.html
總結
以上是生活随笔為你收集整理的c#扩展方法奇思妙用高级篇四:对扩展进行分组管理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Intel i7-13700K全核冲刺6
- 下一篇: preloadlazy load