从系列文章开篇到现在,已经实现的很多扩展了,但过多的扩展会给我们带来很多麻烦,试看下图:

面对这么多“泛滥”的扩展,很多人都会感到很别扭,的确有种“喧宾夺主”的感觉,想从中找出真正想用的方法来太难了!尽管经过扩展后的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 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(); }
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#扩展方法奇思妙用高级篇四:对扩展进行分组管理相关推荐

  1. c#扩展方法奇思妙用高级篇七:“树”通用遍历器

    我的上一篇随笔<c#扩展方法奇思妙用高级篇六:WinForm 控件选择器>中给出了一个WinForm的选择器,其实质就是一个"树"的遍历器,但这个遍历局限于WinFor ...

  2. c# 扩展方法奇思妙用高级篇一:改进 Scottgu 的 In 扩展

    先看下ScottGu对In的扩展: 调用示例1: 调用示例2: 原文地址:New "Orcas" Language Feature: Extension Methods 很多介绍扩 ...

  3. c#扩展方法奇思妙用高级篇八:Type类扩展

    Type 类提供了大量的属性和方法,但在一些基础性开发工作中,Type类功能还有些欠缺,尤其上在处理泛型类型时,如可空类型和泛型集合类型.下面的类就针对这些地方进行扩展.  1     public  ...

  4. C#扩展方法奇思妙用高级篇一:改进 Scottgu 的 In 扩展

    先看下ScottGu对In的扩展: 调用示例1: 调用示例2: 原文地址:New "Orcas" Language Feature: Extension Methods 很多介绍扩 ...

  5. c# 扩展方法奇思妙用变态篇四:string 的翻身革命

    string是各种编程语言中最基础的数据类型,长期以来受尽其它类的压迫,经常被肢解(Substring.Split).蹂躏(Join)... 而现在string要"翻身闹革命"了, ...

  6. c#扩展方法奇思妙用变态篇四:string 的翻身革命

    string是各种编程语言中最基础的数据类型,长期以来受尽其它类的压迫,经常被肢解(Substring.Split).蹂躏(Join)... 而现在string要"翻身闹革命"了, ...

  7. c#扩展方法奇思妙用性能篇一:扩展方法性能初测

    最近写了几篇<c#扩展方法奇思妙用>的文章,一直只是讨论如何扩展.如何使用的问题,几乎没有涉及效率方面. 而大家的回复好多都在问效率如何.性能怎样,也引起了我对效率的关注,今天将初步测试的 ...

  8. c# 扩展方法奇思妙用高级篇六:WinForm 控件选择器

    在Web开发中,jQuery提供了功能异常强大的$选择器来帮助我们获取页面上的对象.但在WinForm中,.Net似乎没有这样一个使用起来比较方便的选择器.好在我们有扩展方法,可以很方便的打造一个. ...

  9. c# 扩展方法奇思妙用高级篇五:ToString(string format) 扩展

    在.Net中,System.Object.ToString()是用得最多的方法之一,ToString()方法在Object类中被定义为virtual,Object类给了它一个默认实现: 1     p ...

最新文章

  1. 计算机系统层次中应用语言级,计算机系统的多级层次结构
  2. C++ std::condition_variable wait() wait_for() 区别
  3. SSH配置启动后Could not instantiate bean class [org.hibernate.cfg.AnnotationConfiguration
  4. [错误记录] --- clickhouse报错Decimal value is too small
  5. linux mysql 存储过程乱码,mysql存储过程中 乱码问题解决办法
  6. 560. 和为K的子数组
  7. 编程高手必学的内存知识02:深入理解栈
  8. [Toolkit]最新Silverlight Toolkit中的DragDrop支持
  9. 川土微 | CA-IS3105W 全集成 DC-DC 转换器
  10. ROS教程(一):ROS安装教程(详细图文)
  11. selenium自动化测试登录qq空间遇到的错误
  12. php漂浮广告代码,JS实现网站悬浮广告的代码
  13. Micropython——基于PYB的霍尔编码器电机的PID控制
  14. 制药企业计算机管理软件,制药企业erp管理系统
  15. [深度学习]动手学深度学习笔记-6
  16. seata的部署与微服务集成(包含多集群异地容灾配置)
  17. 作业1丨创建问答式简历程序
  18. DOS命令不需格式化U盘-FAT32轻松转换成NTFS
  19. Linux ● 文件操作指令
  20. 从张小平这样登月人才的离职事件,看团队期望管理的重要性

热门文章

  1. WinCE电源管理的简单介绍
  2. 汇编 begin_【精品】小学作文500字汇编九篇
  3. 单选按钮带文字_计算机二级MS office高级应用历年真题操作题文字解析
  4. 将excel转为python的字典_python读取excel表并把数据转存为字典
  5. python3 规则引擎_几个常见规则引擎的简单介绍和演示
  6. weblogic jms消息 删除_消息队列与消息中间件概述:消息中间件核心概念与技术选型...
  7. php撒意思,php表示是什么意思
  8. 人工智能泰斗迈克尔·乔丹分享机器学习要义:创新视角,直面挑战
  9. pandas.DataFrame.iterrows
  10. .net Core发布至IIS完全手册带各种踩坑