.NET 开发人员工具: FxCop, GACUtil, NUnit
.NET工具篇(一)—FxCop用法
2013-03-15 07:51 by 左眼微笑右眼泪, 402 阅读, 0 评论, 收藏, 编辑
简介
FxCop是微软提供的一个免费的代码检查的工具,一般的代码检查工具都是扫描你的源代码,但是FxCop是直接扫描你编译后的文件,而不是源代码。我们知道.NET程序编译后成了IL(微软中间语言),并且在每个assembly里面都包含了很多元数据,这些元数据是对代码的描述。通过元数据,它可以获取代码内部的运行情况,进而知道代码的运行时行为,这样它就可以与它设置的规则进行匹配,如果有不匹配的地方,它就提示警告,或者错误。
FxCop一般可以在C:\Program Files\Microsoft SDKs\Windows\v7.0A\FXCop这个文件夹里面找到(Win7 32位机器)。使用之前,需要进行安装,安装完成后双击打开,主界面如下:
它有两个Tab,分别是Targets和Rules,Targets显示的是你要进行检查的程序集,Rules是它自带的检查规则。默认规则如下:
它里面的规则分为了几个部分,这些规则是它推荐的一些规则(后文会有详细的介绍),在使用的时候,可以进行选择,如果不想使用这些规则,就不要在前面打勾,如果你需要一些更高级的规则,FxCop还支持自定义自己的规则,具体的做法可以在网上找到相关的内容。
使用
有两种使用方法,一种是直接打开FxCop,然后把应用程序拖放进行或者通过菜单栏进行打开,打开后,直接点击Analyze这个按钮,就可以进行分析了,分析的结果以列表显示出来。
然后你可以在你的程序中去找这些代码,然后进行改正。上面这种使用方法,用起来不太方便,每次都需要单独进行分析,下面介绍一种方法,把FxCop直接集成到VS中去,可以直接在VS中使用。
首先打开VS2010,“工具”——“外部工具”
会弹出下面的对话框:
点击添加按钮,取名为FxCop,“命令”框输入FxCop的安装目录,参数那里传入不同的值,分别有不同的含义:
./c - 将FXCop分析结果显示在控制台或者IDE的Output窗口中。
· /f:<file/directory> - <file/directory>表示可执行的(EXE)或者动态链接库或者一个目标程序集所在的目录。
· /p:<file> - 表示FXCop项目为一个输入参数,<file>是相应的FXCop工程文件。
· /s - 表示FXCop将在报表中显示摘要。
· /r:<file/directory> - 表示FXCop规则库的位置(通常为“\Microsoft FxCopVersion\Rules”)。在运行过程中这些规则库(.DLL文件)将被从该路径全部加载。
初始目录为FxCop命令行工具的路径,也在安装路径下,一般有以下两种配置:
第一种:先打开FxCop,新建一个空工程,然后保存为SolutionAnalysis.FxCop文件,然后把这个文件放在你的bin目录下,与你要分析的exe放在一个目录下,然后按照下面的这种配置方式配置。然后直接在VS菜单栏“工具”里面运行“FxCop”即可,分析结果会在下面的窗口显示。(添加外部工具成功后,FxCop会自动显示在工具栏下)
这种方式不太好的地方,就是每次需要分析一个exe文件时,都需要新建一个FxCop工程文件然后拷贝过来。比较麻烦,但它这样的好处是,这样可以进行一些单独的设置,可以在FxCop里面设置一些选项,保存为工程文件后,分析的时候就会应用这些设置。
还有另外一种配置情况,就是参数这块有一些不同,这里不需要建工程文件,直接指向exe所在的目录即可。(这种情况下,它就不能单独应用一些设置,它的设置就是FxCop里面的默认的设置)
FxCop规则简介
FxCop里面有很多默认的规则,当然不是所有的都适合,可以根据需要进行选择,下面是对规则的一些简单说明:(在网上找的)
一、 Design(设计)
1. Abstract types should not have constructors
抽象类不应该声明构造方法
2. Assemblies should have valid strong names
程序集应该具有强名称
3. Avoid empty interfaces
避免使用空的接口
4. Avoid excessive parameters on generic types
避免在泛型类中使用过多的类型参数
5. Avoid namespaces with few types
避免让名字空间含有过少的类型
6. Avoid out parameters
避免使用 out类型的参数
7. Collections should implement generic interface
集合类应该实现泛型接口
8. Consider passing base types as parameters
尽量使用基本类型作为参数
9. Declare event handlers correctly
正确的声明事件处理器,事件处理器不应该具有返回值
10. Declare types in namespaces
应该在名字空间里面定义类型,而不是外面
11. Default parameters should not be used
不应该使用参数默认值(C#没有参数默认值)
12. Define accessors for attribute arguments
应该为特性(特性)的构造方法参数定义访问器,其名字跟构造方法参数仅首字母大小写不一样
13. Do not catch general exception types
不要捕捉普通的异常(即System.Exception)
14. Do not declare protected members in sealed types
不要在封闭类型中定义受保护的成员
15. Do not declare static members on generic types
不要在泛型类型中使用静态成员
16. Do not declare virtual members in sealed types
不要在封闭类型中定义虚成员
17. Do not declare visible instance fields
不要定义可见的(public/internal)实例域变量
18. Do not expose generic lists
不要直接暴露范型表
19. Do not hide base class methods
不要隐藏(使用或者不使用new)基类的方法
20. Do not nest generic types in member signatures
不要在成员的签名(参数或者返回值)中嵌套泛型类
21. Do not override operator equals on reference types
不要在引用类型中重载==操作符
22. Do not pass types by reference
不要使用引用(ref or out)传递类型
23. Enum Storage should be Int32
枚举应该是 Int32 类型的
24. Enumerators should be strongly typed
枚举器应该是强类型的
25. Enums should have zero value
枚举应该具有0值
26. Generic methods should provide type parameter
泛型类的方法应该提供类型参数
27. ICollection implementations have strongly typed members
集合接口的实现中应该使用强类型的成员
28. Implement standard exception constructors
自定义的异常应该实现异常类的四个标准构造方法
29. Indexers should not be multidimensional
索引不应该是多维的
30. Interface methods should be callable by child types
接口方法应该可以被子类调用
31. Lists are strongly typed
表应该是强类型的
32. Mark assemblies with assembly version
用程序集版本标示程序集
33. Mark assemblies with CLSCompliant
使用CLSCompliant特性标示程序集
34. Mark assemblies with ComVisible
使用 System.Runtime.InteropServices.ComVisibleAttribute 特性标示程序集
35. Mark attributes with AttributeUsageAttribute
使用 AttributeUsageAttribute 特性标示特性类
36. Mark enums with FlagsAttribute
含有组合的枚举应该使用FlagsAttribute特性标示,相反则不应该
37. Members should not expose certain concrete types
成员(返回值或者参数)不应该暴露具体类型,尽量使用接口
38. Move pinvokes to native methods class
将调用移到本地方法类(不是很理解)
39. Nested types should not be visible
嵌套类型不应该是可见的
40. Override methods on comparable types
可比较类型应该重写 equals 等方法
41. Override operator equals on overriding add and subtract
在重写+和-运算的时候应该同时重写==操作符
42. Properties should not be write only
属性不应该是只写的
43. Provide ObsoleteAttribute message
过时的成员应该使用ObsoleteAttribute特性标示,并提供相应的Message提示使用者
44. Replace repetitive arguments with params array
使用参数数组代替重复的参数
45. Static holder types should be sealed
仅含有静态成员的类型应该声明为封闭的
46. Static holder types should not have constructors
仅含有静态成员的类型应该具有构造方法
47. String uri overloads call system uri overloads
使用string类型的uri参数的重载应调用系统的使用URI类型参数的重载
48. Types should not extend certain base types
类型不应该从具体的类(已经过派生的类)继承,比如异常类不应该从ApplicationException继承,而应该从System.Exception继承
49. Types that own disposable fields should be disposable
含有可释放成员的类型应该是可以释放的(实现IDisposable接口)
50. Types that own native resources should be disposable
使用了非托管资源的类型应该是可以释放的(实现IDisposable接口)
51. Uri parameters should not be strings
Uri 参数不应该是string类型的
52. Uri properties should not be strings
Uri 属性不应该是string类型的
53. Uri return values should not be strings
Uri 类型的返回值不应该是string类型的
54. Use events where appropriate
在适当的时候使用事件
55. Use generic event handler instances
使用泛型的事件处理器实例
56. Use generics where appropriate
在适当的时候使用范型
57. Use integral or string argument for indexers
索引器应该使用整数或者字符串类型的参数
58. Use properties where appropriate
在适当的时候使用属性(而不是以Get或者Set开头的方法)
59. Validate arguments of public methods
对public的方法的参数应该在方法开头处进行检验(比如是否为null的检验)
二、 Globalization(全球化)
1. Avoid duplicate accelerators
避免在顶层控件中使用重复的快捷键(加速键)
2. Do not hardcode locale specific strings
不要对本地的特殊字符串(比如特殊的系统路径)进行硬编码
3. Do not pass literals as localized parameters
不要把文本作为需要本地化的参数直接传递(尽量使用资源文件)
4. Set locale for data types
为某些数据类型设定区域和语言属性(DataSet和DataTable的locale属性)
5. Specify CultureInfo
指定文化信息(地域和语言信息),在调用接受System.Globalization.CultureInfo 类型参数的方法时应该传递文化信息
6. Specify IFormatProvider
指定格式供应器,在调用接受System.IFormatProvider 类型参数的方法时应该传递格式供应器
7. Specify MessageBoxOptions
指定MessageBox的选项,在调用MessageBox.Show方法时应该传递System.Windows.Forms.MessageBoxOptions,特别在某些从右向左阅读习惯的区域
三、 Interoperability(互操作性)
1. Auto layout types should not be ComVisible
自动布局的类型不应该对Com可见(设置System.Runtime.InteropServices.ComVisibleAttribute特性为false)
2. Avoid int64 arguments for VB6 clients
避免使用int64类型,如果成员可能被Visual Basic 6 COM clients调用
3. Avoid non-public fields in ComVisible value types
避免在一个标记有ComVisible特性的值类型里面包含非公有的实例域
4. Avoid overloads in ComVisible interfaces
避免在一个标记有ComVisible特性的接口内声明重载
5. Avoid static members in ComVisible types
避免在一个标记有ComVisible特性的类型
6. Call GetLastError immediately after pinvoke
进行pinvoke以后应该立即使用GetLastError读取错误信息
7. Com registration methods should be matched
Com注册方法(标记有System.Runtime.InteropServices.ComRegisterFunctionAttribute特性的方法)应该是配对的(同时具有一个标记有System.Runtime.InteropServices.ComUnregisterFunctionAttribute的方法与之匹配)
8. Com registration methods should not be visible
Com注册方法应该是不可见的
9. Com visible type base types should be ComVisible
标记有ComVisible特性的类型的基类同样应从标记有ComVisible特性的类继承
10. Com visible types should be creatable
标记有ComVisible特性的类型应该能够使用默认构造器构造
11. Declare PInvokes correctly
正确定义PInvokes
12. Do not use AutoDual ClassInterfaceType
不要把System.Runtime.InteropServices.ClassInterfaceAttribute特性的值设置为System.Runtime.InteropServices.ClassInterfaceType.AutoDual
13. Mark boolean pinvoke arguments with MarshalAs
布尔型的pinvoke参数应该使用System.Runtime.InteropServices.MarshalAsAttribute特性标记
14. Mark ComSource interfaces as IDispatch
将System.Runtime.InteropServices.ComSourceInterfacesAttribute特性标记为System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIDispatch.
15. PInvoke entry points should exist
Pinvoke应该存在入口点
16. PInvokes should not be visible
Pinvoke应该是可见的
四、 Naming(命名)
1. Avoid language specific type names in parameters
避免在参数中使用与特定语言相关的类型(用Uint16代替Ushort)
2. Avoid type names in parameters
避免在外部可见的参数中使用类型名
3. Compound words should be cased correctly
复合词应该使用正确的大小写(不要将Mutlipart写成MultiPart,也不要将ToolBar写成Toolbar,FileName写成Filename)
4. Do not name enum values 'Reserved'
不要在枚举值中使用保留字
5. Do not prefix enum values with type name
不要在枚举值使用类型前缀(比如不要使用Digital之类的前缀)
6. Events should not have before or after prefix
事件的名称不应该包含before和after前缀(尽量使用ing和ed的后缀)
7. Flags enums should have plural names
标记有System.FlagsAttribute特性的枚举应该使用复数形式的名称
8. Identifiers should be cased correctly
标示符(名字空间、类名、属性名、接口名、方法名等)应该使用正确的大小写(通常以大写开头,以后的每个单词都首字母大写)
9. Identifiers should be spelled correctly
标示符应该可以被正确的划分为不同的单词
10. Identifiers should differ by more than case
标示符应该不止有大小写上的不同(因为某些语言是不区分大小写的)
11. Identifiers should have correct prefix
标示符应该使用正确的前缀(接口应该使用字母I开头)
12. Identifiers should have correct suffix
标示符应该使用正确的后缀
System.Attribute/Attribute
System.EventArgs/EventArgs
System.Exception/Exception
System.Collections.ICollection/Collection
System.Collections.IDictionary/Dictionary
System.Collections.IEnumerable/Collection
System.Collections.Queue/Collection or Queue
System.Collections.Stack/Collection or Stack
System.Collections.Generic.ICollection/Collection
System.Collections.Generic.IDictionary/Dictionary
System.Data.DataSet/DataSet
System.Data.DataTable/Collection or DataTable
System.IO.Stream/Stream
System.Security.IPermission/Permission
System.Security.Policy.IMembershipCondition/Condition
An event-handlerdelegate./EventHandler
13. Identifiers should not contain underscores
标示符不应该使用下划线
14. Identifiers should not have incorrect prefix
标示符不应该使用不正确的前缀(比如不应使用一个字母作为前缀)
15. Identifiers should not have incorrect suffix
标示符不应该使用不正确的后缀(不要在不正确的地方使用12中提及的后缀名,和Delegate、Enum、Flags for an enumeration、Impl等后缀名)
16. Identifiers should not match keywords
标示符不应该与系统关键字冲突
17. Long acronyms should be pascal-cased
长度大于等于3的缩写词应该使用pascal的命名规则,即首字母大写
18. Only FlagsAttribute enums should have plural names
只有标记有System.FlagsAttribute特性的枚举的名称才应该使用复数,其他时候应该使用单数
19. Parameter names should match base declaration
派生项的参数名应该同基项相吻合(派生类重写或实现的方法应该同基项具有相同的参数名)
20. Parameter names should not match member names
方法的参数名不应该同类或接口的成员名一样
21. Property names should not match get methods
属性名字不应该同Get开头的方法的名称的后半部分相同
22. Resource string compound words should be cased correctly
包含符合单词的资源字符串应该使用正确的大小写(每个单词的首字母大写)
23. Resource strings should be spelled correctly
资源字符串应该正确的拼写
24. Short acronyms should be uppercase
短的首字母缩写词应该全部大写(比如DB,CR)
25. Type names should not match namespaces
类型的名字不应该与名字空间的名字相同
26. Use preferred terms
优先使用某些项目或者名称,以下这些,后者为优先使用的
ComPlus/EnterpriseServices
Cancelled/Canceled
Indices/Indexes
LogIn/LogOn
LogOut/LogOff
SignOn/SignIn
SignOff/SignOut
Writeable/Writable
五、 Performance(性能规则)
1. Avoid calls that require unboxing
避免调用一个方法,它返回object类型,而你需要的是一个值类型(需要对返回值进行拆箱操作)
2. Avoid costly calls where possible
尽可能的避免进行代价高昂的调用
3. Avoid excessive locals
避免使用过多的局部变量(多于64个,部分可能是编译器生成的)
4. Avoid uncalled private code
避免声明在程序集内从来未被调用的私有成员(private和internal),以下除外:
明确的接口成员
静态构造方法
静态的Main方法(不含参数或仅包含一个string数组的参数的)
序列化构造方法
标记有System.Runtime.InteropServices.ComRegisterFunctionAttribute或者 System.Runtime.InteropServices.ComUnregisterFunctionAttribute.特性的
重写的方法
5. Avoid uninstantiated internal classes
避免声明不会被实例化的内部类,以下情况除外
值类型
抽象类型
枚举
委托
编译器生成的数组类型
仅含有静态成员的内部类
6. Avoid unnecessary string creation
避免创建不必要的string实例(犹指‘通过ToLower和ToUpper创建的string’),含以下情况
对于同一个string实例多次调用ToLower和ToUpper(建议:将返回值赋给一个局部变量,然后使用此局部变量)
使用equals,’==‘,!=比较‘通过ToLower和ToUpper创建的string’(建议:使用String.Compare比较)
向一个System.Collections.Specialized.HybridDictionary类型的成员传递‘通过ToLower和ToUpper创建的string’(建议:HybridDictionary具有一个指示是否忽略大小写的参数的构造方法重载,使用此重载并传递一个true值进去)
7. Avoid unsealed attributes
避免声明未封闭的特性(attributes)(建议:声明为sealed/ NotInheritable-vb.net或者abstract)
8. Avoid unused parameters
避免在方法声明中包含不会被使用的参数,以下情况除外
代理引用的方法
作为事件处理程序的方法
抽象方法(abstract)
虚方法(virtual)
重写的方法(override)
外部方法(extern)
9. Dispose methods should call SuppressFinalize
Dispose方法应该调用SuppressFinalize,以请求系统不要调用其Finalize方法
10. Do not call properties that clone values in loops
不要在循环中使用‘返回一个Clone的对象的属性’(每次返回‘引用不同’的对象,会导致创建大量的相同的对象)
11. Do not cast unnecessarily
不要进行不必要的类型转换(特别是尝试性的转换,建议:在转换前可以使用is操作符来判断转换能够成功)
12. Do not concatenate strings inside loops
不要在循环内串联string(建议:使用StringBuilder代替string)
13. Do not ignore method results
不要忽略方法的返回值(通常调用string的方法会返回新的string)
14. Do not initialize unnecessarily
不要进行不必要的初始化(比如将类成员初始化为它的默认值)
15. Initialize reference type static fields inline
在静态成员声明的时候直接初始化或者调用静态方法初始化(不要使用静态构造方法来初始化静态成员,静态构造方法会影响性能),以下情况除外:
初始化对全局状态的影响是代价高昂的,而且类型在使用前不需要进行初始化的
在不需要访问该类型的静态成员的情况下,全局状态的影响就可以被访问到的
16. Override equals and operator equals on value types
对于公有的值类型,重写equals方法和’==‘操作符(如果你期望用户对实例进行比较或者排序,或者作为哈希表的键)
17. Prefer jagged arrays over multidimensional
使用锯齿形数组代替多维数组(当数组各元素的长度可能不一致时)
注意:公共语言规范(CLS)不支持锯齿数组
18. Properties should not return arrays
公有类型的属性不应该返回数组(数组类型的属性无法进行写保护,即使是只读的,除非每次返回不同的拷贝,但是这样会让调用者产生迷惑。建议:改成方法)
19. Remove empty finalizers
移除空的终结器
20. Remove unused locals
移除未使用过的局部变量
21. Test for empty strings using string length
使用length属性测试字符串是否为空(原因:不要使用==””、==String..Empty、Equals(“”)等方法,使用Length属性的效率是最高的;null==empty比较不会抛出异常;在DotNetFrameWork2里面可以使用IsNullOrEmpty方法来判断字符串是否为null或者empty)
22. Use literals where appropriate
在适当的时候使用const代替static readonly(const是编译时赋值的)
总结
FxCop可以提供对一些常见的错误用法进行分析,如果想要进行高级应用可以自定义一些规则。它默认的一些规则并不是都适用,所以在使用的时候,可以进行适当的选择。如果有时间,可以读一些它默认的规则,在里面可以学到一些你意想不到的知识哦。我个人也不是很常用,只是记录一下配置的过程。欢迎相互交流。
.NET工具篇(二)—GACUtil
2013-03-17 16:22 by 左眼微笑右眼泪, 761 阅读, 0 评论, 收藏, 编辑
在介绍这个工具前,先介绍一下GAC(Gloabal Assembly Cache)全局程序集缓存。对于.NET3.5和以前的版本,GAC通常位于以下目录:C:\Windows\assembly,对于.NET4.0,GAC位于以下目录:C:\Windows\Microsoft.NET\assembly。
GAC到底是个啥呢?如果一个程序集要有多个应用程序访问,那么就必须把这个程序集放到一个已知的目录中,而且CLR在检测到对该程序集的一个引用时,必须知道自动检查该目录。这个已知的目录就叫做GAC。就比如我们经常使用的System.Windows.Forms.dll,System.Data.dll,很多程序都会用到,但是我们却在Bin目录没有找到它们,它们就在GAC里面,应用程序运行的时候,会自动到这个目录中去寻找。
在GAC中“注册”程序集的目录是什么?如果有两家公司分别分成了名称相同的dll,那么显然这两个dll就不能放在同一目录下,但是如果将程序集安装到GAC目录下,就会在GAC目录下创建专门的文件夹,它们会在不同的文件夹里面,就不会出现上面说的这种情况。
先来看看C:\Windows\assembly目录
里面都是我们没有见过的文件类型,只可以看到文件名,点击右键,我们可以卸载这个程序集,但怎么把一个程序放在这个目录下呢。很多人可以想直接放进行就行,确实可以直接拖进去,拖进去它就会把这个文件安装到这个目录下。但这不是建议的做法,建议的方法就是使用即将介绍的工具GACUtil.exe这个工具。因为GAC是目录化的,其中包含很多子目录,在这里看不出来。这些子目录的名称都是通过一个算法生成的,GACUtil.exe这个工具知道GAC的内部结构,并知道如何生成正确的子目录名。GACUtil.exe这个工具一般在C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin目录下。双击这个exe,它会显示一些东西,然后一闪而过,可以通过cmd打开这个工具,界面如下:
上面显示的是它的用法,我们可以通过开关/i和开关/u来分别添加和卸载程序集。注意不能将一个弱命名的程序集放到GAC中去,如果用这个工具安装一个弱命名程序集,它会报错。
如果我们想拷出GAC里面的dll,该怎么办?如果对.NET3.5以下的版本,GAC在这个目录下C:\Windows\Microsoft.NET\assembly,里面的结构我们看不到,也不能拷贝。可以有以下几个方法:
一是使用DOS命令,定位到这个目录下,它会自动显示这个目录下的所有子目录,然后通过命令进行拷贝。可以看到这个目录下面有很多文件夹。通过Copy命令去拷贝里面的dll。
第二种方法就是使用压缩软件,把这个目录放压缩软件的地址栏,按下“确定”就可以看到里面的目录结构,也可以看到里面的文件,直接右键复制文件即可。(这种方法我实验过不行,我在win7中32位中,输入的是.NET3.5框架的GAC目录,不知道是否在win7系统中做了改进)。这里面有一篇介绍的文章,里面还介绍了其他的方法,可以参考下拷出GAC里面的dll
上面的都是对于.NET3.5的GAC目录来操作的,对于.NET4.0,GAC位于以下目录:C:\Windows\Microsoft.NET\assembly。直接打开这个目录:
可以看到,里面直接把目录列出来了,打开里面的目录,出现了很多文件夹,里面可以找到我们经常使用的一些引用:
打开某个目录,可以直接右键复制相应的dll。
由此可知,对于.NET4.0的GAC目录,是可以直接查看文件里面的结构,并且复制里面的文件,而对于.NET3.5的GAC目录,则需要通过其他的方式查看里面的文件。不过不管哪一个版本的GAC目录,往里面添加dll以及卸载dll,最好都使用GACUtil这个工具。
另外微软MSDN上有对这个工具的详细介绍,具体可参考:GACUtil.exe(全局程序集缓存工具)
还有一些需要注意的就是(参考自《CLR via C#》):
1.只能把有强名称的程序集安装到GAC中,否则安装过程中会出错;
2.将程序集安装到GAC中,会破坏一个基本目标:简单地安装,备份,还原,移动和卸载应用程序,所以,建议程序员习题避免全局部署,尽量使用私有部署。
.NET工具篇(三)—NUnit
2013-03-21 07:27 by 左眼微笑右眼泪, 162 阅读, 0 评论, 收藏, 编辑
NUnit是一个开源的.NET下的单元测试工具,可以在它的官方网站上获得它的源码,也可以下载一个安装包。它是一个非常好用的.NET单元测试工具。下面简单介绍一下其用法。
首先在网上下载一个安装包,然后运行安装包,安装完成后,桌面上就会有一个图标,点击,主界面如下:
NUnit的图形界面有三个主要部分,左边是一个树形列表,显示所有的测试,信息和错误消息显示在右上方,堆栈跟踪信息显示在右下方。
要想用NUnit进行单元测试,需要新建两个项目,一个是被测试的项目,一个是用来测试的项目,用来测试的项目必须是类库形式的项目。下面是一个简单的例子,新建了一个被测试的项目,里面有一个类Operation,这个类实现&,|,!以及^运算:
public class Operation
{
public string AndOperation(string s1, string s2)
{
int num1 = Convert.ToInt32(s1, 2);
int num2 = Convert.ToInt32(s2, 2);
int result= num1 & num2;
return Convert.ToString(result, 2).PadLeft(8,'0');
}
public string OrOperation(string s1, string s2)
{
int num1 = Convert.ToInt32(s1, 2);
int num2 = Convert.ToInt32(s2, 2);
int result = num1 | num2;
return Convert.ToString(result, 2).PadLeft(8, '0');
}
public string NoOperation(string s1)
{
int num1 = Convert.ToInt32(s1, 2);
int result = ~num1;
return Convert.ToString(result, 2).PadLeft(8, '0');
}
public string DiffOrOperation(string s1, string s2)
{
int num1 = Convert.ToInt32(s1, 2);
int num2 = Convert.ToInt32(s2, 2);
int result = num1 ^ num2;
return Convert.ToString(result, 2).PadLeft(8, '0');
}
}
下面就对这个类进行测试,测试之前还需要建一个测试用的项目,取名叫做Test,里面新建一个类名为TestOperation,在测试用的项目中,最好是为每个需要测试的类都建一个对应的测试类。TestOperation代码如下:
[TestFixture]
public class TestOperation
{
Operation op = null;
string str = "";
[SetUp]
public void SetUp()
{
op = new Operation();
str = "";
}
[Test]
public void TestAndOperation()
{
string s1 = "";
string s2 = "";
//00101010&00001010
s1="10010001";
s2="00101011";
str = op.AndOperation(s1,s2);
Assert.AreEqual(str,"00000001");
s1 = "11011101";
s2 = "11011011";
str = op.AndOperation(s1, s2);
Assert.AreEqual(str, "11011001");
s1 = "00101010";
s2 = "11100101";
str = op.AndOperation(s1, s2);
Assert.AreEqual(str, "00100000");
s1 = "11010101";
s2 = "00101010";
str = op.AndOperation(s1, s2);
Assert.AreEqual(str, "00000000");
s1 = "11001001";
s2 = "11011101";
str = op.AndOperation(s1, s2);
Assert.AreEqual(str, "11001001");
}
}
在这个类里面,其实只对Operation中的AndOperation这个方法进行了测试。通过这个类的代码,可以看出以下几点:
1.在测试用的类前面必须加上[TestFixture]标识,TestFixture是用来标记一个包含NUnit自动化测试的类。(注意在添加这些标记之前,必须先引用Nunit的类库NUnit.Framework,直接通过添加引用,然后在”.NET”面板中即可找到。)
2.测试的方法前面需要加上[Test]标记,Test是用来表示这是一个需要被调用的自动化测试。给每个测试的方法前面都需要加上这个标识。
3.测试的方法返回类型是Void,并且不接受任何形式的参数。
然后在测试的方法中,直接调用new一个Opertaion对象,然后调用里面的方法(必须添加了对被测试项目的引用),最后用Assert类里面的方法来判断是否命中你想要的结果。Assert类位于NUnit框架的命名空间,包含一些静态方法。比如Assert.IsTrue(判断结果是不是为真),Assert.AreEqual(判断两对象是不是相等),Assert.AreSame(判断两个对象是不是相同的对象)等。选择合适的静态方法,至此,一个简单的测试项目就写好了,直接生成,然后拖放到NUnit中去,点击Run按钮,即可看到测试的结果,红色代表有测试失败的用例,绿色代表全部测试通过。
红色的代表有错误,通过提示,找到代码中相应的测试方法,修改后然后重新测试:
重新测试后没有红色的,都是绿色的,测试就都通过了。
在上面的代码,还看到了一个setup标记,它的作用就相当于构造函数,NUnit在每一次运行测试类中的一个测试之前都会执行setup方法,同样还有另外一个标记teardown,这个标记的作用就相当于析构函数,NUnit在每一次运行测试类中的一个测试之后都会执行teardown方法。另外还有其他的一些特别属性,后面再进行介绍。
NUnit还可以集成到VS中去,打开测试项目用的属性对话框,设置“启动外部程序”:
外部程序就选择你安装的NUnit,命令行参数就写你测试的项目所生成的dll,工作目录就是你测试项目的bin目录,设置之后,把测试用的类库项目设置为启动项,然后运行,它就会自动启动NUnit进行测试。
上面对NUnit测试工具做了一个简单的介绍,后面有机会再深入介绍一下它的其他用法。
什么是GAC?它解决了什么问题?
每一个CLR(Common Language Runtime)所在的计算机都有一个全局程序集缓存(Global Assembly Cache,GAC)。部署在GAC上的程序集必须有一个强名称。一种由.net framework sdk提供的名叫 "Global Assembly Cache tool" (Gacutil.exe)的开发工具,可以把程序集部署到GAC上。GAC存储专门指定的程序集以供计算机上多个应用程序共享。它也为我们提供了克服"DLL地狱"的问题。
百度百科上的解释:
GAC全称是Global Assembly Cache
作用是可以存放一些有很多程序都要用 到的公共Assembly,例如System.Data、System.Windows.Forms等等。这样,很多程序就可以从GAC里面取得
Assembly,而不需要再把所有要用到的Assembly都拷贝到应用程序的执行目录下面。举例而言,如果没有GAC,那么势必每个WinForm程 序的目录下就都要从C:\WINDOWS\Microsoft.NET\Framework\v1.0.3705下面拷贝一份
System.Windows.Forms.dll,这样显然不如都从GAC里面取用方便,也有利于Assembly的升级和版本控制。
除了系统默认放置在GAC中的Assembly如System.Windows.Forms以外,我们也可以添加自己的Assembly:
1)创建一个strong-name的Assembly,例如ToolbarComponent.dll
2)运行gacutil -i ToolbarComponent.dll,把这个Assembly添加到GAC
3)在程序中动态装载:
System.Reflection.Assembly ass=Assembly.Load("ToolbarComponent, Version=1.0.934.20434, Culture=neutral, PublicKeyToken=65f45658c8d4927f");
MessageBox.Show("Is the assembly wwloaded from GAC? "+ass.GlobalAssemblyCache);
在 上面的程序中,ToolbarComponent就是从GAC装载而不是从程序的运行目录下的dll文件中装载,程序目录下不需要放置 ToolbarComponent.dll程序也能正常运行。另外,Assembly.Load()中的参数可以通过 gacutil -l 查到。
另外,上面提到了GAC中的Assembly必须是strong-name的。创建strong-name的Assembly的步骤大致如下:
a) 在命令行运行“sn -k keyPair.snk”创建一个密钥文件。这里的sn.exe也是.NET附带的一个工具。
b) 在VS.NET里面修改“AssemblyInfo.cs”文件:
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("..\\..\\keyPair.snk")]
c) 编译项目,就能得到一个strong-name的Assembly。
MSDN中有一些对GAC的介绍,您可以参考:
1)《Assembly Cache Viewer (Shfusion.dll)》
2)《Global Assembly Cache》
.NET Framework中附带了一些和GAC有关的工具,其中包括:
1)Gacutil.exe,一个命令行的工具,用于在GAC中浏览、添加、删除Assembly
2)Ngen.exe,也是一个命令行的工具,用于在GAC中创建Native Image
3)mscorcfg.msc,一个MMC终端,可以图形化完成Gacutil.exe的主要功能。


















浙公网安备 33010602011771号