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,一经查实,立即删除!

相关文章

js 获取java_js中获取当前时间

var d new Date();var date d.getFullYear() "年" (d.getMonth() 1) "月" d.getDate() "日";var myDate new Date();myDate.getYear(); //获取当前年份(2位)myDate.getFullYear(); //获取完整的年份(4位,1970-????)myDate…

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…

preloadlazy load

最近需要用到预加载和延迟加载的东东&#xff0c;就参考写了一个。 支持跨页面&#xff0c;支持超时设置与依赖设置。 (function($) { (function($) {$.preload function(data, cfg) {return new Loader(data, cfg);};var maps {}, on $.event.add, un $.event.remove, hea…

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

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

java gettime_Java Util.getTime方法代码示例

import org.jrobin.core.Util; //导入方法依赖的package包/类private void initGraphPeriodAndSize(Range range, int width, int height, RrdGraphDef graphDef) {// ending timestamp is the (current) timestamp in seconds// starting timestamp will be adjusted for each…

LESS CSS 框架简介(转)

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

全选 删除 分页

前台&#xff1a; <% Page Language"C#"AutoEventWireup"true"CodeFile"benrenbao.aspx.cs"Inherits"benrenbao"%><% Register Assembly"AspNetPager"Namespace"Wuqi.Webdiyer"TagPrefix"webdiyer&…

计算机java考试_2017年计算机java考试试题

正确的道路是这样&#xff1a;吸取你的前辈所做的一切&#xff0c;然后再往前走。以下是小编为大家搜索整理的2017年计算机java考试试题&#xff0c;希望能给大家带来帮助!更多精彩内容请及时关注我们应届毕业生考试网!1). 程序流程图中的菱形框表示的是( )。A.处理步骤B.逻辑处…

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…

java反射 获取方法_java反射之获取类的信息方法(推荐)

本文接上文“老生常谈反射之class类的使用(必看篇)”&#xff0c;以编写一个用来获取类的信息(成员函数、成员变量、构造函数)的工具类来讲解"反射之获取类的信息"1、获取成员函数信息/*** 获取成员函数信息* param obj*/public static void printclassmethodmessage…

Leetcode 刷题笔记

1Two SumEasy2Add Two NumbersMedium3Longest Substring Without Repeating CharactersMedium5Longest Palindromic SubstringMedium141Linked List CycleEasy转载于:https://www.cnblogs.com/Poceer/p/10922646.html

云计算(Cloud Computing) 培训总结

云计算(Cloud Computing) 培训总结 近来&#xff0c;云计算&#xff08;Cloud Computing&#xff09;是一种新兴的商业模型&#xff0c;在网络中是一个热度很高的新名词&#xff0c;被炒得很火&#xff0c;自己对新技术的那种…

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

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

java怎么复制别人的数据库_java-如何在不使用Apache DDLUtils的情况下使用JDBC将模式从一个数据库复制到另一个数据库?...

我在MySQL中有一个数据库,我想以编程方式在FileMaker Pro中创建所有相同的表和字段.我可以使用JDBC自己完成此操作,但我希望已经有了可以执行此操作的库.我研究了来自Apache的DDLUtils,但无法弄清楚如何构建它(它在构建系统中使用Maven,尝试构建时会出现致命错误).解决方法:我自…

iptables 基础

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

jqgrid学习(1)

grid_id:表格id options&#xff1a;参数 参数描述默认值gridModel当为ture我们会使用colModel中的属性构造查询条件&#xff0c;所用到的参数&#xff1a;name, index, edittype, editoptions, search.还有一个参数&#xff1a;defval&#xff1a;查询条件的默认值&#xff1b…

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基…