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)12288continue; }                
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>扩展的实现:  

ContractedBlock.gifExpandedBlockStart.gifCode
 1    public interface IExtension<V>
 2ExpandedBlockStart.gifContractedBlock.gif    {
 3        V GetValue();
 4    }

 5
 6    public static class ExtensionGroup
 7ExpandedBlockStart.gifContractedBlock.gif    {
 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>
11ExpandedSubBlockStart.gifContractedSubBlock.gif        {
12            return As<T, string>(v);
13        }

14
15        public static T As<T, V>(this V v) where T : IExtension<V>
16ExpandedSubBlockStart.gifContractedSubBlock.gif        {
17            Type t;
18            Type valueType = typeof(V);
19            if (cache.ContainsKey(valueType))
20ExpandedSubBlockStart.gifContractedSubBlock.gif            {
21                t = cache[valueType];
22            }

23            else
24ExpandedSubBlockStart.gifContractedSubBlock.gif            {
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>
33ExpandedSubBlockStart.gifContractedSubBlock.gif        {
34            Type targetInterfaceType = typeof(T);
35            string generatedClassName = targetInterfaceType.Name.Remove(01);
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,
48ExpandedSubBlockStart.gifContractedSubBlock.gif                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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/263944.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Robot Framework-Ride界面介绍及库的添加

Ride界面介绍1. Ride简介1.1什么是RideRide是robotframework的UI界面, 以HTML格式提供易于阅读的结果报告和日志, 用户可以自定义基于Python的测试库, 提供支持selenium的Web测试,语法和python很像。1.2 Ride界面介绍1.2.1主界面介绍&#xff1a; 1.2.2运行按钮和工程目录&a…

Android项目实战(三):实现第一次进入软件的引导页

最近做的APP接近尾声了&#xff0c;就是些优化工作了&#xff0c; 我们都知道现在的APP都会有引导页&#xff0c;就是安装之后第一次打开才显示的引导页面&#xff08;介绍这个软件的几张可以切换的图&#xff09; 自己做了一下&#xff0c;结合之前学过的 慕课网_ViewPager切换…

LESS CSS 框架简介(转)

为什么80%的码农都做不了架构师&#xff1f;>>> 原文地址:http://www.ibm.com/developerworks/cn/web/1207_zhaoch_lesscss/ 简介 CSS&#xff08;层叠样式表&#xff09;是一门历史悠久的标记性语言&#xff0c;同 HTML 一道&#xff0c;被广泛应用于万维网&#…

IP通信基础 实验三

PC端配置&#xff1a;配置ip地址 配置网关 交换机(左)配置&#xff1a;①创建VLAN system-view vlan 10 vlan 20 ②配置PC端接口 interface gi 1/0/1 port link-type access port access vlan 10 interface gi 1/0/2 port link-type access port access vlan 10 interface gi 1…

图像处理(三)

VC实现对不同信号波形相似程度的判别摘要&#xff1a;本文介绍了利用相关对信号波形进行相似程度的判别方法。通过该技术可以对采集到的多种类型的数据信号间的相似度进行判别。本算法由Microsoft Visual C 6.0实现。 一、 引言 在工程上我们经常要判断某设备产生的实际波形信号…

Servlet3.0学习总结(四)——使用注解标注监听器(Listener)

Servlet3.0提供WebListener注解将一个实现了特定监听器接口的类定义为监听器&#xff0c;这样我们在web应用中使用监听器时&#xff0c;也不再需要在web.xml文件中配置监听器的相关描述信息了。 下面我们来创建一个监听器&#xff0c;体验一下使用WebListener注解标注监听器&am…

PHP ThinkPHP学习第一步(搭建及认识ThinkPHP入口文件)

ThinkPHP包下载网址&#xff1a;http://www.thinkphp.cn本人下载3.2版本中的完整版&#xff0c;解压如下取其中的ThinkPHP文件于开发网站的根目录&#xff0c;并建立入口文件index.php入口文件index.php详细内容如下&#xff1a;<?php/** 本文件为thinkPHP的入口文件&#…

iptables 基础

SNAT 和 DNAT 是 iptables 中使用 NAT 规则相关的的两个重要概念。如上图所示&#xff0c;如果内网主机访问外网而经过路由时&#xff0c;源 IP 会发生改变&#xff0c;这种变更行为就是 SNAT&#xff1b;反之&#xff0c;当外网的数据经过路由发往内网主机时&#xff0c;数据包…

Oracle11g远程连接配置 visual studio 2003

服务器端 配置&#xff1a;oracle11g R2 x64 1.设置监听 a.启动栏 -> 开始 -> 程序 -> Oracle-OraDb11g_home1 -> 配置和移植工具 -> Net Manager b.Oracle Net 配置 -> 本地 -> 监听程序 -> LISTENER &#xff0c;查看是否有本地地址在监听&#xff…

JAVA思维导图学习笔记_8张思维导图,55天学习笔记,帮你入门JavaSE

学完了Java中的基础语法&#xff0c;也就是JavaSE。对其做了一个详细的梳理&#xff0c;也便于以后回顾。其中有些知识点被自己遗漏了&#xff0c;比如正则表达式这些&#xff0c;只能以后找机会补上了。01前言对于计算机基础知识的了解、以及Java相关的软件安装。相关笔记02基…

使用MemoryStream和FileStream

使用MemoryStream和FileStream编程访问文件是通过文件流对象进行的&#xff0c;当应用程序需要访问文件时&#xff0c;必须先创建一个文件流对象&#xff0c;此流对象和文件是一一对应关系。在.NET中&#xff0c;使用抽象基类System.IO.Stream代表流&#xff0c;它提供Read和Wr…

WordPress后台的文章、分类,媒体,页面,评论,链接等所有信息中显示ID并将ID设置为第一列...

WordPress后台默认是不显示文章、分类等信息ID的&#xff0c;查看起来非常不方便,不知道Wp团队出于什么原因默认不显示这个但可以使用Simply Show IDs插件来实现 不使用插件,其他网友的实现&#xff1a; <?php /** *为WordPress后台的文章、分类等显示ID From wpdaxue.com …

进程调试--进程启动VS自动附加

程序启动VS自动附加到进程调试 1、 打开注册表regedit 2、 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\currentversion\image file execution options 3、 新建QQ.exe【需要调试的进程名】 4、 右键修改字符串值 5、 F2修改新生成的默认值----debugger 6、右键debugger…

#20175201 实验五 网络编程与安全

一、实验五 网络编程与安全-1 1.实验要求&#xff1a; 两人一组结对编程&#xff1a; &#xff08;1&#xff09;参考http://www.cnblogs.com/rocedu/p/6766748.html#SECDSA &#xff1b; &#xff08;2&#xff09;结对实现中缀表达式转后缀表达式的功能 MyBC.java&#xff1b…

Scala学习思维导图

转载于:https://blog.51cto.com/thunderkeg/1605365

使用git上传代码到github远程仓库

一、新建代码库注册好github登录后&#xff0c;首先先在网页上新建代码库。 点击右上角“&#xff0b;”→New repository 进入如下页面&#xff1a;按照要求填写完成后&#xff0c;点击按钮创建代码库创建成功。 接下来我们查看一下刚刚创建好的代码库&#xff0c;点击右上角的…

java web开发myeclipse_【java项目实战】一步步教你使用MyEclipse搭建java Web项目开发环境(一)...

首先&#xff0c;在开始搭建MyEclipse的开发环境之前&#xff0c;还有三步工具的安装需要完成&#xff0c;只要在安装配置成功之后才可以进入下面的java Web项目开发环境的搭建。1、安装工具第一步&#xff0c;下载并安装JDK&#xff0c;到官网上下载安装即可&#xff0c;之后需…

仿淘宝首页图片切换

资料来源:http://www.cnblogs.com/cloudgamer/archive/2008/07/06/SlideTrans.html?page2#pagedcomment 效果展示: 测试环境:IE8 (待续) 程序代码&#xff1a; 代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR…

NIO学习--缓冲区

Buffer其实就是是一个容器对象&#xff0c;它包含一些要写入或者刚读出的数据。在NIO中加入Buffer对象&#xff0c;体现了新库与原I/O的一个重要区别。在面向流的I/O中&#xff0c;您将数据直接写入或者将数据直接读到Stream对象中。在NIO库中&#xff0c;所有数据都是用缓冲区…

二分图匹配之匈牙利算法

二分图的基本概念&#xff1a; 二分图又称作二部图&#xff0c;是图论中的一种特殊模型。 设G(V,E)是一个无向图&#xff0c;如果顶点V可分割为两个互不相交的子集(A,B)&#xff0c;并且图中的每条边(i&#xff0c;j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j …