.Net下的开源持续集成

谈到持续集成,不如先谈谈集成。软件开发中的集成,通俗地讲就是把各个相关部分的东西组合起来,形成一个可用的软件。比如一个软件项目由几个小组来负责完成,每个小组负责其中一部分功能的实现,比较典型的是在现在的网络游戏开发中,通常有负责引擎的小组,负责游戏逻辑的小组,负责美工的小组,这些小组开发出的东西必须结合在一起才能形成一个可用的游戏;而每个小组内部的每个成员,他们每天在写着不同部分的代码,这些开发人员必须将各自的成果组合起来,才能完成他们共同的目标。从这个意义上讲,从每天每个开发者的日常开发,到各个软件模块的组合拼接,软件集成无所不在,一个完整可用的软件,就是通过不断地集成每个开发人员的代码而形成的。

每个开发过软件的人都能体会到,软件开发绝非一帆风顺,每个人开发出来的代码绝对不会魔术般的自己组合在一起,当新的功能加入到原有软件中的时候,往往不小心破坏了原有的功能,引入了一些bug,当老的bug被修复的时候,又往往会导致其他bug的产生,更糟糕的是,这些bug往往在当时并不能及时被发现。当小组成员们完成了自己所负责的模块,等到最后来一起集成的时候,他们可能已经做好了修复集成问题的心理准备。所有的这一切,都是每个开发人员的切肤之痛。那么,问题到底出在哪了?

让我们不妨换一种思维,让我们不要停留在如何地去修复bug和集成过程中产生的问题,我们渴望的理想状况是,每当我们开发出新的代码并将他们加入原系统中的时候,如果我们能够被及时告知我们是否破坏了原有系统的功能,那么我就能够及时的作出反应,修复这些部分。如果这个过程的粒度足够的细、足够地频繁,我们就能期望每次新功能引入的时候所引起的破坏足够小,并且修复起来足够简单,如果每个开发人员都能够享受到如此的方便并且保证新加入的功能不会影响原有的功能,我们的整个软件过程就能够以一个稳步可靠的步伐,持续增量的向前行进,而不是不断地加入新的代码,然后等到后来bug被人发现的时候被动地去修复它。我想稳步可靠、持续增量的软件过程,是我们每个开发者心目中的理想过程。从开发者的角度来看,毕竟谁也不想经历那种当发现自己的修改破坏的原有的功能的时候的恼火的感受。

持续集成通俗地说就是持续地、频繁地进行集成,每当有新的修改加入的时候,修改的作者能够被及时地告知他的修改是否在引入新的功能的同时保证原有功能的完整。如果整个软件开发团队在一开始就采用这种方式,我们的软件就能被稳步可靠的构建起来。

你也许会有疑问:“你说的只是一种理想状况罢了,谁都希望自己在加入新的代码的时候得知自己的代码是否破坏了原有的功能,但是怎么能够做到这一点,谁有能力来及时地告诉我们哪里有问题?持续集成这个想法不错,但怎么样能够做到持续集成?”我想这些问题是非常好也是关键的问题,持续集成究竟是否可行,如果可行,又该如何执行?我们这里不妨来整理一下,看看究竟什么是持续集成的难点。持续集成的难点主要在于,在新的功能加入的时候,如何来判断整个系统功能仍然完整;出错或者成功,谁来告诉我,如何告诉我;当大家一起协作的时候,如何保证每个人都能够准确地被告知而不会发生混乱。让我们来一个个地分析这些问题。

首先,确保真个系统功能完整性的手段就是测试,如果我们对所有的功能都有完整的测试,那么当新的功能引入的时候,如果某些原有的测试失败,就说明新的修改破坏了原有的功能,而失败的测试就能准确地告诉我们新的修改破坏了哪些原有的功能。其次,持续集成工具将告诉我们集成是否成功,持续集成工具通过运行整个系统中的测试,根据测试的结果来通知开发者,哪些测试失败导致的集成失败。每个软件项目通常会使用版本控制工具例如SVN、CVS,每当有开发者将新的修改加入到系统的代码库中时,持续集成工具会check out出代码库中的最新版本,使用自动化的构建工具例如Ant、Rake等,自动地编译项目中的代码、部署整个应用、准备测试所需的环境和数据、运行所有的测试包括单元测试、功能测试、集成测试等,在整个过程结束后将结果报告出来,持续集成工具会指出任何一个过程中出现的错误,并且准确地报告给开发者。在多人协作的情况下,版本控制工具确保了每个开发者的修改被正确有序地保存,当每个开发者想要提交自己的修改的之前,必须首先确保上一个人所提交的修改被成功集成,才能提交自己的代码,当确保自己的代码被正确集成之后,自己的工作才算完成,否则,就必须修复错误,再次提交,如此反复,直到被成功集成。

开源社区已经为我们提供了非常优秀的持续集成工具,CruiseControl、CruiseControl .Net已成为广泛使用而且非常成熟的持续集成工具,而持续集成所需要的自动化构建工具和版本管理工具如Ant、NAnt、SVN也已经是非常成熟。在下面,我尝试在我的使用经验的感受的基础上,挑选一些比较成熟或者很有潜力的工具,结合自己的使用经验,给大家做一些介绍。 Continue reading “.Net下的开源持续集成”

Windows7 & 8 “用户文件夹”更改位置(更改Users目录位置)

Windows 7和Windows 8的用户文件夹默认所在位置是系统盘(通常是C盘)下的“\Users”目录之内。该文件夹中储存着所有的用户生成文件,比如你保存在“桌面”上的文件(实际上是保存在C:\Users\YourUserName\Desktop目录之中),再比如你保存在“我的文档”里的文件(实际上是保存在C:\Users\UserName\Documents目录之中)。

而随着Windows里安装的软件越来越多,就会有越来越多的“用户生成文件”被保存在“用户文件夹”里。在资源管理器的地址栏里输入“%AppData%”之后回车,就可以看到有多少软件把用户生成数据保存在那里。

用户文件夹处于系统盘的坏处在于,如若系统盘一旦坏掉,就可能连带用户文件一并丢失;其次,由于(随着使用不断生成的)用户文件处于系统盘,也没办法时常备份“干净的系统盘”。

如果能把用户文件夹挪到另外一块儿硬盘上(或者另外一个硬盘分区上),那么系统维护就会容易得多。平时生成的文件(大多数人放在“桌面”、“我的文档”里的文件最多),都被保存在系统盘(或分区)之外;于是随时都可以在不必担心用户文件丢失的情况下重新安装系统(或恢复系统备份)。

注意,以下假设你想把用户文件夹设置在D盘,假定D盘是NTFS分区。

在安装Windows7或者8的过程中,要求输入用户名及密码的时候,先不如输入任何信息,按“Shift+F10”呼出DOS窗口,输入以下命令:

robocopy “C:\Users” “D:\Users” /E /COPYALL /XJ

rmdir “C:\Users” /S /Q

mklink /J “C:\Users” “D:\Users”

而后关闭DOS窗口,按部就班继续安装直至完成。

如此安装的Windows7,所有“用户特殊文件夹”(User Special Folder)的内容都已经被设置在D盘(非系统盘)上。

如果想要移动已安装好的Windows7或者8中的用户文件夹,那么就要按以下步骤操作(稍微麻烦一点,并且过程中可能会出现无法拷贝文件的情况):

  1. 关闭所有应用程序;
  2. 按一下“Windows”键,输入“compmgmt.msc”之后按“Enter”,呼出“计算机管理器”;
  3. 鼠标点击“Administrator”,选择属性,而后在随后的对话框中去掉“帐户已禁用”之前的勾,而后关闭“计算机管理器”;
  4. 注销当前用户(注意,不是“切换用户”),而后以“Administrator”登录
  5. 打开命令行窗口,输入以下命令:robocopy “C:\Users” “D:\Users” /E /COPYALL /XJ /XD “C:\Users\Administrator”
  6. 注销Administrator,重新用你的用户名登录Windows7,而后到“计算机管理器”里禁用Administrator;
  7. 以管理员身份打开一个DOS窗口,输入以下命令:

rmdir “C:\Users” /S /Q

mklink /J “C:\Users” “D:\Users”

搞定。

T-SQL中的格式转换,CAST和CONVERT

在 SQL Server 2012 中将表达式由一种数据类型转换为另一种数据类型。

语法

CAST ( expression AS data_type [ ( length ) ] )
CONVERT ( data_type [ ( length ) ] , expression [ , style ] )

参数

expression

任何有效的表达式。

data_type

目标数据类型。这包括 xml、bigint 和 sql_variant。不能使用别名数据类型。

length

指定目标数据类型长度的可选整数。默认值为 30。

style

指定 CONVERT 函数如何转换 expression 的整数表达式。如果样式为 NULL,则返回 NULL。该范围是由 data_type 确定的。有关详细信息,请参阅“备注”部分。

返回类型

返回转换为 data_type 的 expression。

备注

Date 和 Time 样式

如果 expression 为 date 或 time 数据类型,则 style 可以为下表中显示的值之一。其他值作为 0 进行处理。SQL Server 使用科威特算法来支持阿拉伯样式的日期格式。

不带世纪数位 (yy) (1) 带世纪数位 (yyyy) 标准 输入/输出 (3)
01001、2 默认 mon dd yyyy hh:miAM(或 PM)
1 101 美国 mm/dd/yyyy
2 102 ANSI yy.mm.dd
3 103 英国/法国 dd/mm/yyyy
4 104 德语 dd.mm.yy
5 105 意大利语 dd-mm-yy
6 106 (1) dd mon yy
7 107 (1) Mon dd, yy
8 108 hh:mi:ss
91091、2 默认格式 + 毫秒 mon dd yyyy hh:mi:ss:mmmAM(或 PM)
10 110 美国 mm-dd-yy
11 111 日本 yy/mm/dd
12 112 ISO yymmdd

yyyymmdd

131131、2 欧洲默认格式 + 毫秒 dd mon yyyy hh:mi:ss:mmm(24h)
14 114 hh:mi:ss:mmm(24h)
20120 (2) ODBC 规范 yyyy-mm-dd hh:mi:ss(24h)
21121 (2) ODBC 规范(带毫秒) yyyy-mm-dd hh:mi:ss.mmm(24h)
126 (4) ISO8601 yyyy-mm-ddThh:mi:ss.mmm(无空格)
127(6, 7) 带时区 Z 的 ISO8601。 yyyy-mm-ddThh:mi:ss.mmmZ

(无空格)

130 (1,2) 回历 (5) dd mon yyyy hh:mi:ss:mmmAM
131 (2) 回历 (5) dd/mm/yy hh:mi:ss:mmmAM

1 这些样式值返回不确定的结果。包括所有 (yy)(不带世纪数位)样式和一部分 (yyyy)(带世纪数位)样式。

2 默认值(style010091091311320120 以及 21121)始终返回世纪数位 (yyyy)。

3 转换为 datetime 时输入;转换为字符数据时输出。

4 为用于 XML 而设计。对于从 datetime 或 smalldatetime 到字符数据的转换,其输出格式如上一个表所述。

5 回历是有多种变体的日历系统。SQL Server 使用科威特算法。

重要提示
默认情况下,SQL Server 基于截止年份 2049 年来解释两位数的年份。换言之,就是将两位数的年份 49 解释为 2049,将两位数的年份 50 解释为 1950。许多客户端应用程序(如基于自动化对象的应用程序)都使用截止年份 2030 年。SQL Server 提供了 two digit year cutoff 配置选项以更改 SQL Server 使用的截止年份,从而进行一致的日期处理。建议您指定四位数年份。

6 仅支持从字符数据转换为 datetime 或 smalldatetime。仅表示日期或时间成分的字符数据转换为 datetime 或 smalldatetime 数据类型时,未指定的时间成分设置为 00:00:00.000,未指定的日期成分设置为 1900-01-01。

7 使用可选的时间区域指示符 (Z) 更便于将具有时区信息的 XML datetime 值映射到没有时区的 SQL Server datetime 值。Z 是时区 UTC-0 的指示符。其他时区则以 + 或 – 方向的 HH:MM 偏移量来指示。例如:2006-12-12T23:45:12-08:00。

从 smalldatetime 转换为字符数据时,包含秒或毫秒的样式将在这些位置上显示零。使用相应的 char 或 varchar 数据类型长度从 datetime 或 smalldatetime 值转换时,可截断不需要的日期部分。

从样式包含时间的字符数据转换为 datetimeoffset 时,将在结果末尾追加时区偏移量。

float 和 real 样式

如果 expression 为 float 或 real,则 style 可以为下表中显示的值之一。其他值作为 0 进行处理。

输出
0(默认值) 最多包含 6 位。根据需要使用科学记数法。
1 始终为 8 位值。始终使用科学记数法。
2 始终为 16 位值。始终使用科学记数法。
126, 128, 129 为了保持向后兼容而包括在内,在以后的版本中可能不推荐使用。

money 和 smallmoney 样式

如果 expression 为 money 或 smallmoney,则 style 可以为下表中显示的值之一。其他值作为 0 进行处理。

输出
0(默认值) 小数点左侧每三位数字之间不以逗号分隔,小数点右侧取两位数,例如 4235.98。
1 小数点左侧每三位数字之间以逗号分隔,小数点右侧取两位数,例如 3,510.92。
2 小数点左侧每三位数字之间不以逗号分隔,小数点右侧取四位数,例如 4235.9819。
126 转换为 char(n) 或 varchar(n) 时,等同于样式 2

xml 样式

如果 expression 为 xml,则 style 可以为下表中显示的值之一。其他值作为 0 进行处理。

输出
0(默认值) 使用默认的分析行为,即放弃无用的空格,且不允许使用内部 DTD 子集。

注意
转换为 xml 数据类型时,SQL Server 的无用空格处理方式不同于 XML 1.0。有关详细信息,请参阅创建 XML 数据的实例。
1 保留无用空格。此样式设置将默认的 xml:space 处理方式设置为与指定了 xml:space=”preserve” 的行为相同。
2 启用有限的内部 DTD 子集处理。

如果启用,则服务器可使用内部 DTD 子集提供的以下信息来执行非验证分析操作。

  • 应用属性的默认值。
  • 解析并扩展内部实体引用。
  • 检查 DTD 内容模型以实现语法的正确性。

分析器将忽略外部 DTD 子集。此外,不评估 XML 声明来查看 standalone 属性是设置为 yes 还是 no,而是将 XML 实例当成一个独立文档进行分析。

3 保留无用空格,并启用有限的内部 DTD 子集处理。

二进制样式

如果 expression 为 binary(n)、varbinary(n)、char(n) 或 varchar(n),则 style 可以为下表中显示的值之一。表中没有列出的样式值将返回错误。

输出
0(默认值) 将 ASCII 字符转换为二进制字节,或者将二进制字节转换为 ASCII 字符。每个字符或字节按照 1:1 进行转换。

如果 data_type 为二进制类型,则会在结果左侧添加字符 0x。

1, 2 如果 data_type 为二进制类型,则表达式必须为字符表达式。 expression 必须由数量为偶数的十六进制数字(0、1、2、3、4、5、6、7、8、9、A、B、C、D、E、F、a、b、c、d、e、f)组成。如果将 style 设置为 1,字符 0x 必须为表达式中的前两个字符。如果表达式中包含的字符数为奇数或者包含任何无效的字符,则会引发错误。

如果转换后的表达式长度大于 data_type 长度,则会在右侧截断结果。

如果固定长度 data_types 大于转换后的结果,则会在结果右侧添加零。

如果 data_type 为字符类型,则表达式必须为二进制表达式。每个二进制字符均转换为两个十六进制字符。如果转换后的表达式长度大于 data_type 长度,则会在右侧截断结果。

如果 data_type 为固定大小的字符类型,并且转换后的结果长度小于其 data_type 长度,则会在转换后的表达式右侧添加空格,以使十六进制数字的个数保持为偶数。

对于 style 1,将在转换后的结果左侧添加字符 0x。

CPU精简指令集和复杂指令集的区别

经常看到计算机课程上介绍PowerPC是采用精简指令集的CPU,酷睿CPU是复杂指令集。那么到底精简指令集和复杂指令集有什么区别呢?

以下内容摘自百度百科:

在计算机指令系统的优化发展过程中,出现过两个截然不同的优化方向:CISC技术和RISC技术。CISC是指复杂指令系统计算机(Complex Instruction Set Computer);RISC是指精减指令系统计算机(Reduced Instruction Set Computer)。这里的计算机指令系统指的是计算机的最低层的机器指令,也就是CPU能够直接识别的指令。随着计算机系统的复杂,要求计算机指令系统的构造能使计算机的整体性能更快更稳定。最初,人们采用的优化方法是通过设置一些功能复杂的指令,把一些原来由软件实现的、常用的功能改用硬件的指令系统实现,以此来提高计算机的执行速度,这种计算机系统就被称为复杂指令系统计算机,即Complex Instruction Set Computer,简称CISC。另一种优化方法是在20世纪80年代才发展起来的,其基本思想是尽量简化计算机指令功能,只保留那些功能简单、能在一个节拍内执行完成的指令,而把较复杂的功能用一段子程序来实现,这种计算机系统就被称为精简指令系统计算机.即Reduced Instruction Set Computer,简称RISC。RISC技术的精华就是通过简化计算机指令功能,使指令的平均执行周期减少,从而提高计算机的工作主频,同时大量使用通用寄存器来提高子程序执行的速度。

通过上述内容大家基本可以知道,复杂指令集就是将更多功能步骤集成在了CPU中。例如烹饪西红柿鸡蛋的步骤包括:购买西红柿和鸡蛋,清洗食材,打鸡蛋,炒鸡蛋,放西红柿继续炒,放糖,放盐,炒熟之后盛盘上桌。这样已连续的步骤被集成在CPU内部。外部程序需要烹饪西红柿鸡蛋的时候只需要向CPU下达指令说:“烹饪西红柿鸡蛋”就可以了。复杂指令集就是将琐碎的步骤实现集成到了CPU内部,例如INTEL的SSE3等。

精简指令集就是在CPU中只有容器,调料等基本指令。需要人们在软件中写上如何协调这些指令的步骤,例如你需要向CPU下达指令说:买西红柿,清晰食材,打鸡蛋,炒鸡蛋,放西红柿继续炒,放糖,放盐,炒熟之后盛盘上桌。

由此可见当两个人比赛做饭的时候,复杂指令集的参赛者只需要不停的说“做饭做饭”就可以了。而精简指令集的参赛者需要不停的重复做饭的整个过程和步骤。如果精简指令集的参赛者嘴巴够快才能赶上复杂指令集的参赛者,但是这个“嘴巴”其实取决于内存和CPU之间的带宽是否足够。当然精简指令集也同样存在优越性,复杂指令集要通过不断增加指令的复杂程度和指令的数量来提高性能所以才会越做越大越做越耗电,而精简指令集相比较无疑体积更小更加省电。

上面只是举了一个较为偏激的例子而已,你可以认为是针对于嵌入式精简指令集和普通酷睿(X86)架构的一个比较,这个是不对等和不公平的。IBM PowerPC可是堪比至强性能的呢,这是怎么回事呢?IBM在70年代末期提出复杂指令集存在很多缺点:1,随着应用需求,复杂指令集不可能无止境的增加指令。2,实际程序运行过程中80%的指令,只占一个处理器指令系统的20%,被频繁使用的指令只是一些取、存和加这些最简单的指令。3,CPU厂商不同,复杂指令集也不同,程序的可移植性会非常差,即便可以运行效率也会相差较大(所以我常说服务器要用INTEL处理器,因为更多的程序以调用INTEL指令集为依据)。针对复杂指令集的弊端才提出了精简指令集。

IBM PowerPC无疑拥有更强劲的处理性能与更大的内存带宽,但是其精简指令集的CPU决定了只能在某个特定应用领域才能完胜至强。因为PowerPC本身硬件设计和软件设计更具备针对性。例如并发控制、数据存取。

总结:精简指令集也好,复杂指令集也罢都是CPU运行功能和实现应用的方法而已。在特定领域和行业选择不同指令集CPU才是王道。手机和火星登陆车里边永远无法放进去X86,个人家用电脑日趋强劲的多应用多功能化也永远不要奢望使用IBM PowerPC。

有人说精简指令集架构平台下对于程序员的要求会更高呢?其实未必。在写程序时精简指令集和复杂指令集开发代码上并没有特别大的差异,真正转换成特定CPU指令架构底层语言是依靠编译器实现的。也就是你的代码技能在精简指令集用,也能在复杂指令集用,主要取决于你的编译器编译出的汇编语言适用于何种架构。

马化腾:腾讯14年,制胜的7个维度

编者按:腾讯公司首席执行官马化腾近日在《牛津管理评论》分享了他一直以来的思考。这些思考来自腾讯14年来的经验和教训。马化腾说,在腾讯内部的产品开发和运营过程中,有一个词一直被反复提及,那就是”灰度”。他很尊敬的企业家前辈任正非也曾经从这个角度有深入思考,并且写过《管理的灰度》,这里所提倡的灰度,主要是内部管理上的妥协和宽容。
以下为马化腾分享的全文:
从去年合作伙伴大会到现在,已经过去了一年。这一年里,我们大家一起向一个开放的、没有疆界的互联网新生态迈出了第一步。大量的创业伙伴在腾讯开放平台上涌现出来,其中不少团队还取得了初步成功。 Continue reading “马化腾:腾讯14年,制胜的7个维度”

冒择入希快归堆等九种排序算法介绍

昨晚听尚学堂的一个视频,其中主持人有说到几种排序算法的记忆口诀“冒择路(入)兮(希)快归堆”,抽着中午休息的空档,我在这里把这七种排序算法,外加另外2种:桶式排序和基数排序,一并使用Java语言进行简单的介绍。

冒泡排序,选择排序,插入排序,稀尔排序,快速排序,归并排序,堆排序,桶式排序,基数排序

一、冒泡排序(BubbleSort)

1. 基本思想:

两两比较待排序数据元素的大小,发现两个数据元素的次序相反时即进行交换,直到没有反序的数据元素为止。 Continue reading “冒择入希快归堆等九种排序算法介绍”

.NET中如何使用嵌入的资源

.Net中嵌入资源(位图、图标或光标等)有两种方式,一是直接把资源文件加入到项目,作为嵌入资源,在代码中通过Assembly的GetManifestResourceStream方法获取资源的Stream。另一种方法是在项目中加入. resx资源文件,在资源文件中添加资源,由ResourceManager类统一管理其中的资源。下面分别详述这两种方法:

使用GetManifestResourceStream读取嵌入资源

  1. 加入资源文件

    直接把要嵌入到程序集的资源文件加入到项目中,可以加在项目的根目录,可以加在项目的任何目录中。

  2. 设置资源文件的“BuildAction”属性

    将嵌入资源文件的“BuildAction”属性设置为“Embedded Resource”

  3. 代码中使用嵌入资源

//获得正在运行类所在的名称空间

Type type = MethodBase.GetCurrentMethod().DeclaringType;

string _namespace = type.Namespace;

//获得当前运行的Assembly

Assembly _assembly = Assembly.GetExecutingAssembly();

//根据名称空间和文件名生成资源名称

string resourceName = _namespace + ".directory.BitmapManifest.bmp";

//根据资源名称从Assembly中获取此资源的Stream

Stream stream = _assembly.GetManifestResourceStream(resourceName);

Image myImage = Image.FromStream(stream);

上述代码中有部分步骤是为了获得resourceName的值,resourceName的值结构形式类似于:”命名空间.目录路径.文件名+后缀”

此外还有一种访问资源的格式:”assembly://SpringSample/Sample.Spring/Resources.BitmapManifest.bmp”,有点像URI格式,不同的是协议为”assembly://”,”SpringSample”为程序集名称,”Sample.Spring”为默认命名空间,”Resources”为目录路径。注意:程序集名称与默认命名空间之间、默认命名空间与目录路径之间用”/”分隔,目录路径与文件名之间用”.”分隔,因为如此我们理解了访问资源的路径方式,就可以直接进行引用了。

使用. resx资源文件嵌入资源

  • 新建资源文件

    在项目中新建一个资源文件,资源文件以.resx为后缀,同时还会新建一个跟资源文件同名的Designer.cs文件。
    其实资源文件最大的用处是用来做多语言版本的软件时保存不同语言的资源,比如不同语言的菜单文本,可以把不同语言的字符串放在同一个资源类型下的不同资源包中,程序运行时根据运行时系统的culture选择不同的包显示不同语言的字符串。
    新建了资源文件后就能往资源文件中添加资源文件:

    资源中可以添加字符串、位图、图标、音频、文件等等的资源。
    添加的资源都会被保存在项目的Resources文件夹中。

  • 设置资源文件的“BuildAction”属性

    Resources文件夹中的所有资源文件的“BuildAction”属性设置为“Embedded Resource”。

  • 资源存在方式

    .resx资源文件管理的资源可以用两种存在形式,一种是以一般的文件形式存在于Resources文件夹中,另一个是经过Base64编码后嵌入到.resx资源文件中。
    打开.resx资源文件,选择资源,在属性中Persistence属性决定资源的存在形式。资源的两种存在形式,在代码中调用都是一样的。

  • 代码中使用嵌入资源
    Icon myIcon = (Icon)Resource1.ResourceManager.GetObject("IconTest");
    Icon myIcon = Resource1.MyIcon
  • 多语言的资源应用

    //得到当前语言环境
    CultureInfo ci = Thread.CurrentThread.CurrentCulture;
    //CultureInfo ci = System.Globalization.CultureInfo.CurrentCulture;
    
    Icon myIcon = (Icon)Resource1.ResourceManager("IconText", ci);

设计模式:抽象工厂模式(Abstract Factory)

概述

在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时由于需求的变化,往往存在着更多系列对象的创建工作。如何应对这种变化?如何绕过常规的对象的创建方法(new),提供一种“封装机制”来避免客户程序和这种“多系列具体对象创建工作”的紧耦合?这就是我们要说的抽象工厂模式。

意图

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

模型图

逻辑模型:

Continue reading “设计模式:抽象工厂模式(Abstract Factory)”

浅谈C#中子类用new与override的区别

我们先看代码和运行结果,代码:

BaseClass.cs文件:

    public class BaseClass
    {
        public BaseClass()
        {
            Console.WriteLine("Base Constructed Function");

        }

        public virtual void Execute()
        {
            Console.WriteLine("Base Virtual Execute Function");
        }

        public virtual void Execute2()
        {
            Console.WriteLine("Base Virtual Execute2 Function");
        }

    }

ChildClass.cs文件:

    public class ChildClass : BaseClass
    {
        public ChildClass()
        {
            Console.WriteLine("Children Constructed Function");
        }

        public override void Execute()
        {
            Console.WriteLine("Children Execute Function");
        }

        public new void Execute2()
        {
            Console.WriteLine("Children Execute2 Function");
        }

    }

Program.cs文件:

    class Program
    {
        static void Main(string[] args)
        {
            #region 直接创建子类对象
            ChildClass A = new ChildClass();
            A.Execute();
            A.Execute2();
            #endregion

            #region 使用父类型引用子类型
            BaseClass B = new ChildClass();
            B.Execute();
            B.Execute2();
            #endregion

            Console.ReadKey();
        }
    }

运行结果:

我们有发现:

C# override,重写,是指对父类中的虚方法(标记virtual)或抽象方法(标记为abstract)进行重写,实现新的功能,它必须与父类方法的签名完全一致,而且与父类方法的可访问性也必须一致,此关键字不可以用于重写非虚方法和静态方法,。

new,隐藏,是指在子类中重新定义一个签名与父类方法相同的方法,父类的同名方法只是被隐藏而未被覆盖,这个方法也可以不显示使用new修饰,只是编译时会弹出一个警告信息:如果是有意隐藏,请使用关键字new。

SqlServer,MySql如何获得最新自增长字段的值

在向数据库中多个表同时插入修改数据的时候,我们1般需要使用事务来保证完整性,这段时间在维护1个银行项目,其中有较多的同时插入修改多张数据表的场景,作者使用的主键是nvarchar类型,由C# Guid.NewGuid()方法每次生成随机GUID进行填充,然后将该GUID作为主键与关链键(外键)同时插入多张数据表中。

根据我有限的数据库原理知识,这样的设计可以保证记录的唯一性,毕竟GUID重复的机率比宇宙灭亡还难见到,但这样设计会降低查询速率,因为我们知道自增长的主键,在设计的时候使用了数据结构中的顺序存储结构,即我们只需要知道0记录的地址A,第20条主键记录的位置就在A+(主键数据类型长度*20)上,查询效率可谓是数据库索引中最快的,而nvarchar主键是很慢的,不过主键应该都是簇索引的吧。

那么我们在同时向多张数据表中插入数据,并且需要维护外键的时候,该如何获得之前插入的最后1个自增长数据值呢?总不能”select top 1 * from tablename order by pkname desc”吧?其实不必这么复杂,数据库设计者们给我们提供了更简单的方法:

SQL SERVER中的三种获得自增长ID的方法

先看例子:

      insert into users(Code,name) values('1111','aaaa')
      select  SCOPE_IDENTITY()
      select  @@IDENTITY
      select  IDENT_CURRENT('users')

三种方法的区别:

SCOPE_IDENTITY()
返回插入到同一作用域中的 IDENTITY 列内的最后一个 IDENTITY 值。一个作用域就是一个模块――存储过程、触发器、函数或批处理。因此,如果两个语句处于同一个存储过程、函数或批处理中,则它们位于相同的作用域中。

@@IDENTITY
返回最后插入的标识值,跨表跨会话。

IDENT_CURRENT(表名)
返回为任何会话和任何作用域中的指定表最后生成的标识值。这个函数需要一个以表名为值的变量,也就是说虽然不受会话和作用域的限制,却会受到表的限制。

体会:加上事物处理,两个函数一个变量没有本质区别。不加事物处理两个函数一个变量受到其他会话、作用域的影响不一样。

MySQL中获得自增长的方法

在MySQL中,使用auto_increment类型的id字段作为表的主键。通常的做法,是通过“select max(id) from tablename”的做法,但是显然这种做法需要考虑并发的情况,需要在事务中对主表加以“X锁“,待获得max(id)的值以后,再解锁。这种做法需要的步骤比较多,有些麻烦,而且并发性也不好。有没有更简单的做法呢?答案之一是通过select LAST_INSERT_ID()这个操作。乍一看,它和select max(id)很象,但实际上它是线程安全的。也就是说它是具体于数据库连接的。下面通过实验说明:

  1. 在连接1中向A表插入一条记录,A表包含一个auto_increment类型的字段。
  2. 在连接2中向A表再插入一条记录。
  3. 结果:在连接1中执行select LAST_INSERT_ID()得到的结果和连接2中执行select LAST_INSERT_ID()的结果是不同的;而在两个连接中执行select max(id)的结果是相同的。

其实在MSSQL中SCOPE_IDENTITY()和IDENT_CURRENT()的区别和这里是类似的。使用SCOPE_IDENTITY()可以获得插入某个IDENTITY字段的当前会话的值,而使用IDENT_CURRENT()会获得在某个IDENTITY字段上插入的最大值,而不区分不同 的会话。

注意:使用select last_insert_id()时要注意,当一次插入多条记录时,只是获得第一次插入的id值。