[C#]Fast Reflection Library

mikel阅读(1001)

全文英文版:Fast Reflection Library

  这是我在CodePlex上创建的一个项目,它的网址是http://www.codeplex.com/FastReflectionLib,使用Microsoft Public License (Ms-PL),您可以随意在自己的产品中使用它的全部或部分代码。这个项目用到了我在《方法的直接调用,反射调用与Lambda表达式调用》和《这下没理由嫌Eval的性能差了吧?》两篇文章里用到的做法,并加以提炼和扩展发布的项目——随便搞搞,留个印记,也供以后参考。

基本使用方式

   反射是.NET中非常重要的功能。使用反射来构造对象、调用方法或是访问属性是某些项目中常用的做法之一(例如ORM框架)。众所周知,与一个成员的直 接访问相比,反射调用的性能要低好几个数量级。FastReflectionLib提供了一种简便的方式,使一些常用反射调用的性能得到大幅提高。如下:

using System;
using System.Reflection;
using FastReflectionLib;
namespace SimpleConsole
{
class Program
{
static void Main(string[] args)
{
PropertyInfo propertyInfo = typeof(string).GetProperty("Length");
MethodInfo methodInfo = typeof(string).GetMethod("Contains");
string s = "Hello World!";
// get value by normal reflection
int length1 = (int)propertyInfo.GetValue(s, null);
// get value by the extension method from FastReflectionLib,
// which is much faster
int length2 = (int)propertyInfo.FastGetValue(s);
// invoke by normal reflection
bool result1 = (bool)methodInfo.Invoke(s, new object[] { "Hello" });
// invoke by the extension method from FastReflectionLib,
// which is much faster
bool result2 = (bool)methodInfo.FastInvoke(s, new object[] { "Hello" });
}
}
}

  在得到了PropertyInfo或MethodInfo对象之后,我们可以使用GetValue或Invoke方法来访问属性或调用方法。在 FastReflectionLib中为PropertyInfo、MethodInfo等对象定义了对应的扩展方法,于是我们就可以使用这些扩展方法 (从代码上看来,基本上只是在原来的方法之前加上“Fast”)来进行调用,与之前的方法相比,新的扩展方法性能有极大的提高。

直接使用各工作对象

  各FastXxx方法实际上是将PropertyInfo等对象作为Key去一个Cache中获取对应的工作对象,然后调用工作对象上对应的方法。因此,直接调用工作对象可以获得更好的性能。各工作对象类型的对应关系如下:

  • PropertyInfo:IPropertyAccessor
  • MethodInfo:IMethodInvoker
  • ConstructorInfo:IConstructorInvoker
  • FieldInfo:IFieldAccessor

  我们可以使用FastReflectionCaches.MethodInvokerCache来获取一个IMethodInvoker对象:

static void Execute(MethodInfo methodInfo, object instance, int times)
{
IMethodInvoker invoker = FastReflectionCaches.MethodInvokerCache.Get(methodInfo);
object[] parameters = new object[0];
for (int i = 0; i < times; i++)
{
invoker.Invoke(instance, parameters);
}
}

工作对象的默认实现与扩展

  在FastReflectionLib中,已经提供了IPropertyAccessor等接口的默认实现。该实现将会构造一颗表达式树 (Expression Tree)并将其编译(调用其Compile方法)以获得一个与反射方法签名相同的委托对象。这是一种简单、通用而安全的实现,由于Compile方法使 用了Emit,其性能也较为令人满意(可见下面的性能测试)。但是这并不是性能最高的做法,如果使用Emit生成最优化的代码,其性能甚至会高于方法的直 接调用(例如Dynamic Reflection Library)。如果您想使用更好的实现来替换,则可以自行构造一个工作对象接口的实现,并替换对应的Factory:

public class BetterPropertyAccessor : IPropertyAccessor
{
public BetterPropertyAccessor(PropertyInfo propertyInfo) { ... }
...
}
public class BetterPropertyAccessorFactory :
IFastReflectionFactory<PropertyInfo, IPropertyAccessor>
{
public IPropertyAccessor Create(PropertyInfo key)
{
return new BetterPropertyAccessor(key);
}
}
class Program
{
static void Main(string[] args)
{
FastReflectionFactories.PropertyAccessorFactory =
new BetterPropertyAccessorFactory();
...
}
}

缓存的默认实现与扩展

  在FastReflectionLib中使用基于 System.Collections.Generic.Dictionary<TKey, TValue>类型编写的缓存容器。每次调用FastXxx扩展方法时,类库将从对应的缓存容器中获取工作对象。如果缓存容器中还没有所需的工作对 象,那么它就会调用合适的Factory来构造新的工作对象。从下面的性能测试来看,许多时间是消耗在缓存查找上的,如果您有更好的缓存实现,可以使用以 下的方法替换默认的缓存的容器:

public class BetterMethodInvokerCache :
IFastReflectionCache<MethodInfo, IMethodInvoker>
{
public IMethodInvoker Get(MethodInfo key) { ... }
}
class Program
{
static void Main(string[] args)
{
FastReflectionCaches.MethodInvokerCache =
new BetterMethodInvokerCache();
...
}
}

根据需要自行缓存工作对象

  FastReflectionLib中通过PropertyInfo等对象作为Key,对PropertyAccessor等工作对象进行缓 存。但是在某些场景下,您也可以选择合适的方式来自行缓存工作对象。与FastReflectionLib源码同时发布的CustomCache示例网站 中包含了一个FastEval扩展,在某些场景下,我们可以使用这个更高效的方法来替换内置的Eval方法。这个示例的特点如下:

  • 使用对象的类型和属性名同时作为缓存的Key获取对应的PropertyAccessor对象
  • 使用PropertyAccessor获取“匿名对象”中的属性值
  • 缓存的作用域为特定页面,而不是整个AppDomain。

性能测试

  FastReflectionLib源码中包含了一个性能测试项目,您可以从中看出FastReflectionLib对于反射的性能改进。摘录部分数据如下(测试在我的笔记本上运行,Release编译)。

  执行以下方法:

public class Test
{
public void MethodWithArgs(int a1, string a2) { }
}

  进行一百万次调用,结果如下:

调用方式 消耗时间(秒)
方法直接调用 0.0071397
内置反射调用 1.4936181
工作对象调用 0.0468326
Fast方法调用 0.1373712

[Lucene] Lucene.net多字段(Fields)、多索引目录(IndexSearcher

mikel阅读(570)

Lucene.net是目前在.net环境中被普遍使用的全文索引的开源项目,这次在项目的开发中也使用它进行全文索引。
在开发过程中碰到一些小问题就是对多字段和多索引目录进行搜索。
1、多字段搜索就是同时要一个以上的字段中的内容进行比较搜索,类似概念在SQL中就是select * from Table where a like '%query%' or b like '%query%'。
Lucene.net中的单个字段查询大家都比较熟悉,这里对字段content进行搜索
Query query = QueryParser.Parse(querystr,"content",new ChineseAnalyzer());
Hits hits = searcher.Search(query);
对多个字段查询用到一个MultiFieldQueryParser对象,该对象继承自Query,我们要对字段title,content进行搜索。
string[] fields = {"content","title"};
Query multiquery = MultiFieldQueryParser.Parse(querystr,fields,new ChineseAnalyzer());
Hits hits = searcher.Search(multiquery);
2、多索引目录就是要在多个索引目录的中进行比较搜索,类似概念在SQL中就是select * from TableA union select * from TableB。
IndexSearcher[] searchers = new IndexSearcher[2];
searchers[0] = new IndexSearcher(IndexPath0);
searchers[1] = new IndexSearcher(IndexPath1);
MultiSearcher multisearcher = new MultiSearcher(searchers);
TopDocs multitopdocs = multisearcher.Search(query, null, 1000);
这个搜索的结果可能有相同的信息,比如你有一条相同的信息在多个目录中索引,搜索的结果就会出现多次相同的信息。
还有一种搜索方式是用到ParallelMultiSearcher这个对象,它是从MulitSearcher继承而来。
ParallelMultiSearcher parallelmultisearcher = new ParallelMultiSearcher(searchers);
TopDocs paralleltopdocs = parallelmultisearcher.Search(query, null, 1000);
这个搜索是对搜索后的结果进行合并,剔除重复的信息。

[Lucene]多线程操作建议和[Lucene.Net] 分页显示

mikel阅读(735)

对于并发,Lucene.Net 遵循以下规则:
1. 允许任意多的读操作并发,即任意数量用户可同时对同一索引做检索操作。
2. 即便正在进行索引修改操作(索引优化、添加文档、删除文档),依然允许任意多的检索操作并发执行。
3. 不允许并发修改操作,也就是说同一时间只允许一个索引修改操作。
Lucene.Net 内部已经对多线程安全进行了处理,打开 IndexWrite.cs / IndexReade.csr 文件,会发现很多操作都使用了 lock 进行多线程同步锁定。只要遵循一定的规则,就可以在多线程环境下安全运行 Lucene.Net。
建议:
1. Directotry、Analyzer 都是多线程安全类型,只需建立一个 Singleton 对象即可。
2. 所有线程使用同一个 IndexModifier 对象进行索引修改操作。
3. IndexWriter/IndexReader/IndexModifier/IndexSearcher 最好使用同一个 Directory 对象,否则多线程并发读写时可能引发 FileNotFoundException。
IndexModifier 对象封装了 IndexWriter 和 IndexReader 的常用操作,其内部实现了多线程同步锁定。使用 IndexModifier 可避免同时使用 IndexWriter 和 IndexReader 时需要在多个对象之间进行同步的麻烦。等所有修改操作完成后,记住调用 Close() 方法关闭相关资源。并不是每次操作都需要调用 Optimize(),可以依据特定情况,定期执行优化操作。

[文字]苹果CEO乔布斯的九大法则

mikel阅读(737)

  法则一:网罗一流人才

  乔布斯说,他花了半辈子时间才充分意识到人才的价值。他在最近一次讲话中说:“我过去常常认为一位出色的人才能顶两名平庸的员工,现在我认为能 顶50名。”由于苹果公司需要有创意的人才,所以乔布斯说,他大约把四分之一的时间用于招募人才。高级管理人员往往能更有效地向人才介绍本公司的远景目 标。而对于新成立的富有活力的公司来说,其创建者通常在挑选职员时十分仔细,老板亲临招聘现场,则可使求职者以最快速度了解与适应公司的文化氛围和环境。

  法则二:一切要尽在掌控

  在乔布斯的哲学里,苹果始终是,也必须是一家能“全盘掌控”的公司。从硬件到软件,从设计到功能,从操作系统到应用软件,苹果的产品全部由自己 打造,我们随时可以改变,创新每天都在发生,我们关注产品中的每一项技术,只有这样,每一项创新才能顺利的变为产品。苹果的创新就在于我们能够掌握每一个 零件。

  法则三:没有B计划

  对于乔布斯来说:只有A计划,在进入一个新的领域时,只倾注全力打造一款产品或服务,没有备选方案,没有退路。这样才能将最好的创意、技术、设计倾注到一款产品上。如iPod、iPhone(手机上网)莫不如此。

  法则四:追求残忍的完美

  曾经有过这样的报道:新产品的一切工作都已完成,只待发布。乔布斯仅仅因为一个不起眼的细节(如:还有两颗螺丝暴露在表面)而要求一切推倒重来。正是这个残忍的标准成就了苹果一个个令人惊叹的产品。

  法则五:软件永远是核心技术

  乔布斯意识到,“对未来的消费类电子产品而言,软件都将是核心技术”。坚持做操作系统和那些悄无声息的后端软件,比如iTunes。

  这样的苹果才不至于像DELL、惠普或索尼那样,因为等待微软最新操作系统的发布而延迟推出硬件产品。这样的苹果不用看着微软干着急。而随意修改系统,还可以为iPhone和iPod制作特别的版本。这也是消费电子巨头索尼在随身听市场不敌苹果的原因。

  法则六:谨慎与第三方合作

  在乔布斯眼里,与其与平庸的公司合作不如不合作,那只会降低苹果的产品的品质。另外受他人制约是不可容忍的。

  法则七:秘而不宣

  乔布斯把苹果公司的新产品研发计划看成是这个星球的最高机密,保密程度足与FBI媲美。乔布斯在2005年6月的苹果全球开发者大会上宣布全面 转向英特尔CPU时,才透露早在5年前,苹果已将操作系统MACOSX的代码按X86的架构重写,之前没有任何风声。还有一例:作为iPhone的首个销 售商,AT&T旗下Cingular的高层也是在正是发布的两周前才看到iPhone的真实原型机,苹果甚至专门制作了几款假的iPhone原型 机以掩人耳目。

  法则八:产品必须带来可观利润

  乔布斯认为:又酷又新的产品不能带来可观利润,那不是创新,只是艺术。很显然,乔布斯在1997年重返苹果,第一件事就是砍掉经营了七年还在不 断亏损的牛顿(Newton)PDA业务。尽管这个产品极为创新、另人惊叹。而乔布斯在重返苹果后打造的iMac、iPod、iPhone都是又酷又赚钱 的产品。

  法则九:科技产品必须是令人惊叹且引导消费的

  在乔布斯眼里:满足客户需求是平庸公司所为,引导客户需求是高手之道。领先数步没有成为先烈,还为人疯狂喜爱并大掏腰包。乔布斯带领下的苹果做 到了,糖果设计师设计的iMac出现后,人们才认识到电脑外壳原来可以是彩色的、透明的。iPod的可人设计+在线购买的iTunes音乐商店打造了全程 的音乐体验。iPhone的发布让大家发现手机是可以没有键盘和触摸笔的,最好的操作工具是与生俱来、不会遗失、操作自如的手指。

  还有报道说,乔布斯在推新产品时从来不会请调研公司进行市场调研。乔布斯认为那只会看到表面现象,让新产品的研发误入歧途。

  当然,所有的法则都是为两个字服务——“创新”。

[MVC]ASP.NET MVC 1.0 RC的发布,

mikel阅读(685)

Scott Gu宣布了ASP.NET MVC 1.0 RC的发布, Scott Gu在blog上写了一篇ASP.NET MVC 1.0 Release Candidate Now Available,

RC版本有很多小的改进:

  • Visual Studio同 MVC的集成更成熟了,也就说有了更好用的脚手架- "Go to Controller" / "Go to View" / "Add View" 等等
  • AJAX 的改进
  • FileUpload / "Download" 改进,例如 FileResult映射到 Fileupload
  • Visual Studio T4 模板,这就意味着自己可以去定制MVC模板,顺便在这里介绍一个T4:Visual Studio 2008包含了一个文本模板转换工具箱,又称为T4,是领域特定语言工具的一部分,并可用于基于文本模板的方式来生成代码。中文的介绍可见InfoQ的《Visual Studio 2008的文本模板转换工具箱

如果你已经安装beta,必须先卸载Beta版,如果你还用到MVC Feature部分请到codeplex上下载一个新版本,具体参看Phil的 blog

发行说明中有详细的从beta升级到rc的详细清单,是升级的必读文件。

如果我想保留ASP.NET MVC 1.0 beta,想直接使用ASP.NET mvc 1.0 rc的二进制版本,可以通过命令行提取MSI文件中的内容,命令如下:

msiexec /a AspNetMVCRC-setup.msi

等待几秒钟后就可以在C:\Program Files\Microsoft ASP.NET\ASP.NET MVC RC 目录下获取到二进制版本,而不会影响到你的Visual Studio模板 和GAC。

[历史]《时代》杂志:苹果发展史上10大辉煌时刻

mikel阅读(903)

美国知名杂志《时代》近日评出了“苹果发展史上10大辉煌时刻”,其中包括创业之初、苹果I现身、Mac机诞生、iPod音乐播放器风潮、iPhone智能手机受追捧等等。以下为《时代》杂志所评出“苹果发展史上10大辉煌时刻”简介:

 

1、创业之初

在正式创建苹果之前,史蒂夫·乔布斯(Steve Jobs)刚刚21岁。当时乔布斯经常同他的两位好友史蒂夫·沃兹尼亚克(Steve Wozniak)、罗纳德·韦恩(Ronald Wayne)一起玩耍。或许这三人本来可组建一家摇滚乐队,只是这三位好友另有想法。

《时代》杂志:苹果发展史上10大辉煌时刻

当时乔布斯和韦恩同在一名为Atari的公司工作,而沃兹尼亚克在惠普工作。1976年4月 1,这三位好友创建了苹果电脑公司。虽然苹果的最初创始人为三位,但仅仅三个月之后,韦恩就以800美元的低廉价格将自己所持苹果股份出售。由此一来,人 们谈起苹果创始人时,多数情况下只提及乔布斯和沃兹尼亚克两人。

2、苹果I现身

如今回头一看,苹果生产的第一款电脑Apple I看上去就是老古董。Apple I由沃兹尼亚克亲自手工打造,并于1976年在加州一次展会上展出。Apple I硬件放在木盒子里边,外观上看上去是一台打字机。该产品并没有配备显示器,需外接电视机来作为显示设备。其售价为666.66美元(据说是沃兹尼亚克喜 欢重复输入某个数字)。

《时代》杂志:苹果发展史上10大辉煌时刻

Apple I共生产了200台,消费者需自己购买相应键盘、显示器等配套设备。目前Apple I价格已涨至数万美元,并成为古董收藏家最为青睐的对象之一。

3、1984年:Mac机的黎明

《时代》杂志:苹果发展史上10大辉煌时刻

Mac机是“Macintosh”品牌的简称。1984年1月24日,乔布斯首先在苹果股东 年度大会上展示这款机型。首款Mac机售价为2495美元。它是首款公众有能力购买、并采用了图形用户界面的个人电脑。为打开Macintosh机销路, 苹果还聘请了著名导演雷德利·斯科特(Ridley Scott,为著名科幻恐怖片《异形》导演)专门制作了广告。

4、PowerPC处理器

《时代》杂志:苹果发展史上10大辉煌时刻

1981年,美国科技巨头IBM正式进入个人电脑市场。随后数年中,苹果同IBM之间展开了 激烈市场竞争。6年之后,苹果同IBM(还有摩托罗拉参与)共同开发了PowerPC处理器。当时苹果同IBM结盟,主要是为了对付微软-英特尔联盟。在 2006年之前,所有苹果电脑都采用PowerPC处理器。2006年之后,苹果也投向英特尔阵营。

5、乔布斯归来

《时代》杂志:苹果发展史上10大辉煌时刻

1985年,乔布斯被苹果董事会“扫地出门”,他随后创办一家名为NeXT的软件开发公司。 上个世纪90年代,由于微软Windows操作系统不断蚕食苹果市场份额,1997年8月,苹果宣布收购NeXT。乔布斯由此而回到了苹果,并重新执掌该 公司。自那时以来,乔布斯已成了苹果的代名词。乔布斯今年1月14日宣布,自己将病休5个月。此消息一出,立即导致苹果股价当天下跌4%。

6、iMac发布

《时代》杂志:苹果发展史上10大辉煌时刻

1998年,苹果推出了首款iMac个人电脑,外观颜色为蓝色。后来该公司陆续推出了多种颜 色的iMac。这种颜色鲜艳的外观设计深受消费者欢迎。在iMac上市第一年期间,就成为美国市场销量最高的个人电脑。此外,iMac还开启了苹果在产品 名称前面中增加英文小写字母“i”的历史。

7、iPod风潮

《时代》杂志:苹果发展史上10大辉煌时刻

2001年10月,苹果推出了其iPod音乐播放器。自那时以来,该公司已推出了10款不同 型号的iPod,其中一些支持视频播放,另一些则体积更小,如iPod Nano就是如此。截止去年4月底,全球iPod总销量已突破1亿部。在2007年美国所有出售的汽车中,70%配备了iPod连接功能。

8、Mac OS X操作系统

《时代》杂志:苹果发展史上10大辉煌时刻

乔布斯创建NeXT后,曾积累了一些操作系统开发的经验。乔布斯重回苹果怀抱后,就在 NeXT技术基础上开发了Mac OS X(注:X是罗马字母“10”之意,而非英文字母X)操作系统。该产品于2001年3月发布。OS X的系统稳定性、高处理速度及华丽界面都因素,都成为苹果进行市场宣传的重点所在。

9、iTunes音乐商店

《时代》杂志:苹果发展史上10大辉煌时刻

在苹果于2001年推出其iPod音乐播放器的同时,还开通了与之相配套的网络音乐服务iTunes网上商店。到2003年时,iTunes音乐商店可供下载的歌曲数量已达500万首,电视剧和电影数量分别为350部和400部。目前iTunes已成为全球最为热门的网络音乐商店之一。

10、iPhone智能手机

《时代》杂志:苹果发展史上10大辉煌时刻

2007年夏季,美国乃至全球最关注的并不是好莱坞大片,而是苹果推出的iPhone智能手 机。该产品提供音乐播放、电子邮件收发、互联网接入等功能。去年7月,苹果又推出了3G版iPhone。在2G版和3G版iPhone首发期间,全球各国 都出现了消费者提前数天排队购买现象。

[C#]C#正则表达式整理备忘

mikel阅读(1059)

有一段时间,正则表达式学习很火热很潮流,当时在CSDN一天就能看到好几个正则表达式的帖子,那段时 间借助论坛以及Wrox Press出版的《C#字符串和正则表达式参考手册》学习了一些基础的知识,同时也为我在CSDN大概赚了1000分,今天想起来,去找《C#字符串和正 则表达式参考手册》时,已经不知所踪了。现在用到正则的时候也比较少,把以前的笔记等整理一下,以志不忘。
(1)“@”符号
符下两ows表研究室的火热,当晨在“@”虽然并非C#正则表达式的“成员”,但是它经常与C#正则表达式出双入对。“@”表示,跟在它后面的字符串是个“逐字字符串”,不是很好理解,举个例子,以下两个声明是等效的:
string x="D:\\My Huang\\My Doc";
string y = @"D:\My Huang\My Doc";
事实上,如果按如下声明,C#将会报错,因为“\”在C#中用于实现转义,如“\n”换行:
string x = "D:\My Huang\My Doc";

(2)基本的语法字符。
\d  0-9的数字
\D  \d的补集(以所以字符为全集,下同),即所有非数字的字符
\w  单词字符,指大小写字母、0-9的数字、下划线
\W  \w的补集
\s  空白字符,包括换行符\n、回车符\r、制表符\t、垂直制表符\v、换页符\f
\S  \s的补集
.  除换行符\n外的任意字符
[…]  匹配[]内所列出的所有字符
[^…]  匹配非[]内所列出的字符
下面提供一些简单的示例:


string i = "\n";
string m = "3";
Regex r 
= new Regex(@"\D");
//同Regex r = new Regex("\\D");
//r.IsMatch(i)结果:true
//r.IsMatch(m)结果:false

string i = "%";
string m = "3";
Regex r 
= new Regex("[a-z0-9]");
//匹配小写字母或数字字符
//r.IsMatch(i)结果:false
//r.IsMatch(m)结果:true

(3)定位字符
“定位字符”所代表的是一个虚的字符,它代表一个位置,你也可以直观地认为“定位字符”所代表的是某个字符与字符间的那个微小间隙。
^  表示其后的字符必须位于字符串的开始处
$  表示其前面的字符必须位于字符串的结束处
\b  匹配一个单词的边界
\B  匹配一个非单词的边界
另外,还包括:\A  前面的字符必须位于字符处的开始处,\z  前面的字符必须位于字符串的结束处,\Z  前面的字符必须位于字符串的结束处,或者位于换行符前
下面提供一些简单的示例:


string i = "Live for nothing,die for something";
Regex r1 
= new Regex("^Live for nothing,die for something$");
//r1.IsMatch(i) true
Regex r2 = new Regex("^Live for nothing,die for some$");
//r2.IsMatch(i) false
Regex r3 = new Regex("^Live for nothing,die for some");
//r3.IsMatch(i) true

string i = @"Live for nothing,
die for something
";//多行
Regex r1 = new Regex("^Live for nothing,die for something$");
Console.WriteLine(
"r1 match count:" + r1.Matches(i).Count);//0
Regex r2 = new Regex("^Live for nothing,die for something$", RegexOptions.Multiline);
Console.WriteLine(
"r2 match count:" + r2.Matches(i).Count);//0
Regex r3 = new Regex("^Live for nothing,\r\ndie for something$");
Console.WriteLine(
"r3 match count:" + r3.Matches(i).Count);//1
Regex r4 = new Regex("^Live for nothing,$");
Console.WriteLine(
"r4 match count:" + r4.Matches(i).Count);//0
Regex r5 = new Regex("^Live for nothing,$", RegexOptions.Multiline);
Console.WriteLine(
"r5 match count:" + r5.Matches(i).Count);//0
Regex r6 = new Regex("^Live for nothing,\r\n$");
Console.WriteLine(
"r6 match count:" + r6.Matches(i).Count);//0
Regex r7 = new Regex("^Live for nothing,\r\n$", RegexOptions.Multiline);
Console.WriteLine(
"r7 match count:" + r7.Matches(i).Count);//0
Regex r8 = new Regex("^Live for nothing,\r$");
Console.WriteLine(
"r8 match count:" + r8.Matches(i).Count);//0
Regex r9 = new Regex("^Live for nothing,\r$", RegexOptions.Multiline);
Console.WriteLine(
"r9 match count:" + r9.Matches(i).Count);//1
Regex r10 = new Regex("^die for something$");
Console.WriteLine(
"r10 match count:" + r10.Matches(i).Count);//0
Regex r11 = new Regex("^die for something$", RegexOptions.Multiline);
Console.WriteLine(
"r11 match count:" + r11.Matches(i).Count);//1
Regex r12 = new Regex("^");
Console.WriteLine(
"r12 match count:" + r12.Matches(i).Count);//1
Regex r13 = new Regex("$");
Console.WriteLine(
"r13 match count:" + r13.Matches(i).Count);//1
Regex r14 = new Regex("^", RegexOptions.Multiline);
Console.WriteLine(
"r14 match count:" + r14.Matches(i).Count);//2
Regex r15 = new Regex("$", RegexOptions.Multiline);
Console.WriteLine(
"r15 match count:" + r15.Matches(i).Count);//2
Regex r16 = new Regex("^Live for nothing,\r$\n^die for something$", RegexOptions.Multiline);
Console.WriteLine(
"r16 match count:" + r16.Matches(i).Count);//1
//对于一个多行字符串,在设置了Multiline选项之后,^和$将出现多次匹配。

string i = "Live for nothing,die for something";
string m = "Live for nothing,die for some thing";
Regex r1 
= new Regex(@"\bthing\b");
Console.WriteLine(
"r1 match count:" + r1.Matches(i).Count);//0
Regex r2 = new Regex(@"thing\b");
Console.WriteLine(
"r2 match count:" + r2.Matches(i).Count);//2
Regex r3 = new Regex(@"\bthing\b");
Console.WriteLine(
"r3 match count:" + r3.Matches(m).Count);//1
Regex r4 = new Regex(@"\bfor something\b");
Console.WriteLine(
"r4 match count:" + r4.Matches(i).Count);//1
//\b通常用于约束一个完整的单词

 (4)重复描述字符
“重复描述字符”是体现C#正则表达式“很好很强大”的地方之一:
{n}  匹配前面的字符n次
{n,}  匹配前面的字符n次或多于n次
{n,m}  匹配前面的字符n到m次
?  匹配前面的字符0或1次
+  匹配前面的字符1次或多于1次
*  匹配前面的字符0次或式于0次
以下提供一些简单的示例:


string x = "1024";
string y = "+1024";
string z = "1,024";
string a = "1";
string b="-1024";
string c = "10000";
Regex r 
= new Regex(@"^\+?[1-9],?\d{3}$");
Console.WriteLine(
"x match count:" + r.Matches(x).Count);//1
Console.WriteLine("y match count:" + r.Matches(y).Count);//1
Console.WriteLine("z match count:" + r.Matches(z).Count);//1
Console.WriteLine("a match count:" + r.Matches(a).Count);//0
Console.WriteLine("b match count:" + r.Matches(b).Count);//0
Console.WriteLine("c match count:" + r.Matches(c).Count);//0
//匹配1000到9999的整数。

 (5)择一匹配
C#正则表达式中的 (|) 符号似乎没有一个专门的称谓,姑且称之为“择一匹配”吧。事实上,像[a-z]也是一种择一匹配,只不过它只能匹配单个字符,而(|)则提供了更大的范 围,(ab|xy)表示匹配ab或匹配xy。注意“|”与“()”在此是一个整体。下面提供一些简单的示例:


string x = "0";
string y = "0.23";
string z = "100";
string a = "100.01";
string b = "9.9";
string c = "99.9";
string d = "99.";
string e = "00.1";
Regex r 
= new Regex(@"^\+?((100(.0+)*)|([1-9]?[0-9])(\.\d+)*)$");
Console.WriteLine(
"x match count:" + r.Matches(x).Count);//1
Console.WriteLine("y match count:" + r.Matches(y).Count);//1
Console.WriteLine("z match count:" + r.Matches(z).Count);//1
Console.WriteLine("a match count:" + r.Matches(a).Count);//0
Console.WriteLine("b match count:" + r.Matches(b).Count);//1
Console.WriteLine("c match count:" + r.Matches(c).Count);//1
Console.WriteLine("d match count:" + r.Matches(d).Count);//0
Console.WriteLine("e match count:" + r.Matches(e).Count);//0
//匹配0到100的数。最外层的括号内包含两部分“(100(.0+)*)”,“([1-9]?[0-9])(\.\d+)*”,这两部分是“OR”的关系,即正则表达式引擎会先尝试匹配100,如果失败,则尝试匹配后一个表达式(表示[0,100)范围中的数字)。

(6)特殊字符的匹配
下面提供一些简单的示例:


string x = "\\";
Regex r1 
= new Regex("^\\\\$");
Console.WriteLine(
"r1 match count:" + r1.Matches(x).Count);//1
Regex r2 = new Regex(@"^\\$");
Console.WriteLine(
"r2 match count:" + r2.Matches(x).Count);//1
Regex r3 = new Regex("^\\$");
Console.WriteLine(
"r3 match count:" + r3.Matches(x).Count);//0
//匹配“\”

string x = "\"";
Regex r1 = new Regex("^\"$");
Console.WriteLine("r1 match count:" + r1.Matches(x).Count);//1
Regex r2 = new Regex(@"^""$");
Console.WriteLine(
"r2 match count:" + r2.Matches(x).Count);//1
//匹配双引号

 (7)组与非捕获组
以下提供一些简单的示例:


string x = "Live for nothing,die for something";
string y = "Live for nothing,die for somebody";
Regex r 
= new Regex(@"^Live ([a-z]{3}) no([a-z]{5}),die \1 some\2$");
Console.WriteLine(
"x match count:" + r.Matches(x).Count);//1
Console.WriteLine("y match count:" + r.Matches(y).Count);//0
//正则表达式引擎会记忆“()”中匹配到的内容,作为一个“组”,并且可以通过索引的方式进行引用。表达式中的“\1”,用于反向引用表达式中出现的第一个组,即粗体标识的第一个括号内容,“\2”则依此类推。

string x = "Live for nothing,die for something";
Regex r 
= new Regex(@"^Live for no([a-z]{5}),die for some\1$");
if (r.IsMatch(x))
{
    Console.WriteLine(
"group1 value:" + r.Match(x).Groups[1].Value);//输出:thing
}

//获取组中的内容。注意,此处是Groups[1],因为Groups[0]是整个匹配的字符串,即整个变量x的内容。

string x = "Live for nothing,die for something";
Regex r 
= new Regex(@"^Live for no(?<g1>[a-z]{5}),die for some\1$");
if (r.IsMatch(x))
{
    Console.WriteLine(
"group1 value:" + r.Match(x).Groups["g1"].Value);//输出:thing
}

//可根据组名进行索引。使用以下格式为标识一个组的名称(?<groupname>…)。

string x = "Live for nothing nothing";
Regex r 
= new Regex(@"([a-z]+) \1");
if (r.IsMatch(x))
{
    x 
= r.Replace(x, "$1");
    Console.WriteLine(
"var x:" + x);//输出:Live for nothing
}

//删除原字符串中重复出现的“nothing”。在表达式之外,使用“$1”来引用第一个组,下面则是通过组名来引用:
string x = "Live for nothing nothing";
Regex r 
= new Regex(@"(?<g1>[a-z]+) \1");
if (r.IsMatch(x))
{
    x 
= r.Replace(x, "${g1}");
    Console.WriteLine(
"var x:" + x);//输出:Live for nothing
}


string x = "Live for nothing";
Regex r 
= new Regex(@"^Live for no(?:[a-z]{5})$");
if (r.IsMatch(x))
{
    Console.WriteLine(
"group1 value:" + r.Match(x).Groups[1].Value);//输出:(空)
}

//在组前加上“?:”表示这是个“非捕获组”,即引擎将不保存该组的内容。

 (8)贪婪与非贪婪
正则表达式的引擎是贪婪,只要模式允许,它将匹配尽可能多的字符。通过在“重复描述字符”(*,+)后面添加“?”,可以将匹配模式改成非贪婪。请看以下示例:


string x = "Live for nothing,die for something";
Regex r1 
= new Regex(@".*thing");
if (r1.IsMatch(x))
{
    Console.WriteLine(
"match:" + r1.Match(x).Value);//输出:Live for nothing,die for something
}

Regex r2 
= new Regex(@".*?thing");
if (r2.IsMatch(x))
{
    Console.WriteLine(
"match:" + r2.Match(x).Value);//输出:Live for nothing
}

(9)回溯与非回溯
使用“(?>…)”方式进行非回溯声明。由于正则表达式引擎的贪婪特性,导致它在某些情况下,将进行回溯以获得匹配,请看下面的示例:


string x = "Live for nothing,die for something";
Regex r1 
= new Regex(@".*thing,");
if (r1.IsMatch(x))
{
    Console.WriteLine(
"match:" + r1.Match(x).Value);//输出:Live for nothing,
}

Regex r2 
= new Regex(@"(?>.*)thing,");
if (r2.IsMatch(x))//不匹配
{
    Console.WriteLine(
"match:" + r2.Match(x).Value);
}

//在r1中,“.*”由于其贪婪特性,将一直匹配到字符串的最后,随后匹配“thing”,但在匹配“,”时失败,此时引擎将回溯,并在“thing,”处匹配成功。
在r2中,由于强制非回溯,所以整个表达式匹配失败。

(10)正向预搜索、反向预搜索
正向预搜索声明格式:正声明 “(?=…)”,负声明 “(?!…)” ,声明本身不作为最终匹配结果的一部分,请看下面的示例:


string x = "1024 used 2048 free";
Regex r1 
= new Regex(@"\d{4}(?= used)");
if (r1.Matches(x).Count==1)
{
    Console.WriteLine(
"r1 match:" + r1.Match(x).Value);//输出:1024
}

Regex r2 
= new Regex(@"\d{4}(?! used)");
if (r2.Matches(x).Count==1)
{
    Console.WriteLine(
"r2 match:" + r2.Match(x).Value); //输出:2048
}

//r1中的正声明表示必须保证在四位数字的后面必须紧跟着“ used”,r2中的负声明表示四位数字之后不能跟有“ used”。

反向预搜索声明格式:正声明“(?<=)”,负声明“(?<!)”,声明本身不作为最终匹配结果的一部分,请看下面的示例:


string x = "used:1024 free:2048";
Regex r1 
= new Regex(@"(?<=used:)\d{4}");
if (r1.Matches(x).Count==1)
{
    Console.WriteLine(
"r1 match:" + r1.Match(x).Value);//输出:1024
}

Regex r2 
= new Regex(@"(?<!used:)\d{4}");
if (r2.Matches(x).Count==1)
{
    Console.WriteLine(
"r2 match:" + r2.Match(x).Value);//输出:2048
}

//r1中的反向正声明表示在4位数字之前必须紧跟着“used:”,r2中的反向负声明表示在4位数字之前必须紧跟着除“used:”之外的字符串。

(11)十六进制字符范围
正则表达式中,可以使用 "\xXX" 和 "\uXXXX" 表示一个字符("X" 表示一个十六进制数)形式字符范围:
\xXX       编号在 0到255 范围的字符,比如:空格可以使用 "\x20" 表示。
\uXXXX   任何字符可以使用 "\u" 再加上其编号的4位十六进制数表示,比如:汉字可以使用“[\u4e00-\u9fa5]”表示。


(12)对[0,100]的比较完备的匹配
下面是一个比较综合的示例,对于匹配[0,100],需要特殊考虑的地方包括
*00合法,00.合法,00.00合法,001.100合法
*空字符串不合法,仅小数点不合法,大于100不合法
*数值是可带后缀的,如“1.07f”表示该值为一个float类型(未考虑)

Code

(13)精确匹配有时候是困难的
有些需求要做到精确匹配比较困难,例如:日期、Url、Email地址等,其中一些你甚至需要研究一些专门的文档写出精确完备的表达式,对于这种情况,只 能退而求其次,保证比较精确的匹配。例如对于日期,可以基于应用系统的实际情况考虑一段较短的时间,或者对于像Email的匹配,可以只考虑最常见的形 式。

[C#]CodeSmith生成SQL Server视图的实体类脚本

mikel阅读(1065)

第一个文件是businessobjctforView.cst,生成C#实体类,此脚本需要引用第二个文件CommonUtility.cs。需将两个文件放在同一目录中。

<%@ CodeTemplate Language="C#" TargetLanguage="T-SQL"
      Description
="Generates a update stored procedure." %>
<%@ Assembly Name="SchemaExplorer" %> 
<%@ Import Namespace="SchemaExplorer" %> 
<%@ Property Name="SourceTable" Type="SchemaExplorer.ViewSchema"
      Category
="Context"
      Description
="Table that the stored procedures should be based on." %>
<%@ Assembly Src="CommonUtility.cs" %>
<%@ Import Namespace="Common.Data" %>
<script runat="template">
    CommonUtility rule
=new CommonUtility();
</script>
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;                                                                                                                                                         
namespace SOA.Model
{
    
/// <summary>
    
/// <%= SourceTable.Name %> object for View table '<%= SourceTable.Name %>'.
    
/// </summary>
    [Serializable]
    
public class <%= SourceTable.Name %>
    {
    
#region Private Member
        
<% for (int i = 0; i < SourceTable.Columns.Count; i++) { %>
        
private <%= rule.GetCSharpVariableType(SourceTable.Columns[i]) %> _<%= SourceTable.Columns[i].Name %>;
    
<% } %>
    
#endregion
        
    
#region Constructor
        
public <%= SourceTable.Name %>() {}        
        
        
public <%= SourceTable.Name %>(
                
<% for (int i = 0; i < SourceTable.Columns.Count1; i++) { %>
        
<%= rule.GetCSharpVariableType(SourceTable.Columns[i]) %> <%= SourceTable.Columns[i].Name %>,    
    
<% } %>
    
<%= rule.GetCSharpVariableType(SourceTable.Columns[SourceTable.Columns.Count1]) %> <%= SourceTable.Columns[SourceTable.Columns.Count1].Name %>
        ) 
        {
        
<% for (int i = 0; i < SourceTable.Columns.Count; i++) { %>
         _
<%= SourceTable.Columns[i].Name %> = <%= SourceTable.Columns[i].Name %>;
    
<% } %>
        }
    
#endregion
    
    
#region Public Properties
    
<% for (int i = 0; i < SourceTable.Columns.Count; i++) { %>
        
public <%= rule.GetCSharpVariableType(SourceTable.Columns[i]) %> <%= rule.GetCamelCaseName(SourceTable.Columns[i].Name) %>
        {
            
get { return _<%= SourceTable.Columns[i].Name %>; }
            
set { _<%= SourceTable.Columns[i].Name %> = value; }
        }
    
<% } %>
    
#endregion
    }
}
 
第二个文件是CommonUtility.cs,此文件是包含C#的数据类型定义以及输出:

using System;
using System.Text;
using CodeSmith.Engine;
using SchemaExplorer;
using System.ComponentModel;
using System.Data;
namespace Common.Data
{
    
/**//// <summary>
    
/// TemplateRule
    
/// </summary>
    public class CommonUtility
    {
        
//get Columns info by TableName
        public ViewColumnSchemaCollection GetColumnCollectionByTable(ViewSchema table)
        {
            ViewColumnSchemaCollection columns 
= new ViewColumnSchemaCollection(table.Columns);
            
return columns;
        }
        
//Get camelcase name,such as Customer,
        public string GetCamelCaseName(string str)
        {
            
return str.Substring(0,1).ToUpper()+str.Substring(1);
        }
        
       
//Get ,user,private const String USER_FIELD = "User"
        public string GetMemberConstantDeclarationStatement(ColumnSchema column)
        {
            
return GetMemberConstantDeclarationStatement("public const String ",column);
        }
        
        
//such as public const String USER_TABLE = "User"
        public string GetTableConstantDeclarationStatement(ViewSchema table)
        {
            
return GetMemberConstantDeclarationStatement("public const String ",table);    
        }
        
//suck as USER_TABLE
        public string GetUpperStatement(ViewSchema table)
        {
            
return     table.Name.ToUpper()+"_TABLE";
       }
        
//suck as USER_FIELD
        public string GetUpperStatement(ColumnSchema column)
       {
           
return column.Name.ToUpper()+"_FIELD";
        }
        
// such as USER_TABLE = "User"
        public string GetMemberConstantDeclarationStatement(string protectionLevel, ViewSchema table)
        {
            
return protectionLevel+GetUpperStatement(table)+" = "+GetCamelCaseName(table.Name)+"";
        }
       
        
//such as USERID_FIELD = "Userid"
        public string GetMemberConstantDeclarationStatement(string protectionLevel,ColumnSchema column)
        {
            
return protectionLevel+GetUpperStatement(column)+" = "+GetCamelCaseName(column.Name)+"";
        }
        
public string GetCSharpVariableType(ViewColumnSchema column)
        {
            
switch(column.DataType)
            {
                
case DbType.AnsiString: return "string";
                
case DbType.AnsiStringFixedLength: return "string";
                
case DbType.Binary: return "byte[]";
                
case DbType.Boolean: return "bool";
                
case DbType.Byte: return "int";
                
case DbType.Currency: return "decimal";
                
case DbType.Date: return "DataTime";
                
case DbType.DateTime: return "DateTime";
                
case DbType.Decimal: return "decimal";
                
case DbType.Double: return "double";
                
case DbType.Guid: return "Guid";
                
case DbType.Int16: return "short";
                
case DbType.Int32: return "int";
                
case DbType.Int64: return "long";
                
case DbType.Object: return "object";
                
case DbType.SByte: return "sbyte";
                
case DbType.Single: return "float";
                
case DbType.String: return "string";
                
case DbType.StringFixedLength: return "string";
                
case DbType.Time: return "TimeSpan";
                
case DbType.UInt16: return "ushort";
                
case DbType.UInt32: return "uint";
                
case DbType.UInt64: return "ulong";
                
case DbType.VarNumeric: return "decimal";
            }
            
            
return null;
        }
        
        
public string GetCSharpBaseType(ViewColumnSchema column)
       {
            
switch(column.DataType)
           {
                
case DbType.AnsiString: return "System.String";
                
case DbType.AnsiStringFixedLength: return "System.String";
                
case DbType.Binary: return "System.Byte[]";
                
case DbType.Boolean: return "System.Boolean";
                
case DbType.Byte: return "System.Int32";
                
case DbType.Currency: return "System.Decimal";
                
case DbType.Date: return "System.DataTime";
                
case DbType.DateTime: return "System.DataTime";
                
case DbType.Decimal: return "System.Decimal";
                
case DbType.Double: return "System.Double";
                
case DbType.Guid: return "System.Guid";
                
case DbType.Int16: return "System.Int16";
                
case DbType.Int32: return "System.Int32";
                
case DbType.Int64: return "System.Int64";
                
case DbType.Object: return "System.Object";
                
case DbType.SByte: return "System.SByte";
                
case DbType.Single: return "System.Single";
                
case DbType.String: return "System.String";
                
case DbType.StringFixedLength: return "System.String";
                
case DbType.Time: return "System.TimeSpan";
                
case DbType.UInt16: return "System.UInt16";
                
case DbType.UInt32: return "System.UInt32";
                
case DbType.UInt64: return "System.UInt64";
                
case DbType.VarNumeric: return "System.Decimal";
            }
            
return null;
        }
    }
}