下图是Visual Studio提供的代代码分析的11种规范。
(图-1)
开发规范是每软件开发公司都有的,可有每个公司在开发方面都积累了适合自己的一套规范,有的可能是通用的,有的可能是根据实际情况适合自己的。比如,公司要求所有的方法的第一个字符必须是大写,字段不能定义成公有,可能对其他公司来说这些规范不适合,但对于本公司来说,就是一种规范,是一种要求。(这里是举个例子,不是实际存在的公司规范)
公司的开发规范是这样要求的,但对于一些新手,或一些其他公司跳过来的程序员,能很快习惯吗?当然,通过开会,指正,再开会,再指正,开发人员的习惯会形成,但又没有更好的办法呢?用一种量化的约定来让开发人员尽快适应本公司的开发规范呢?答案是肯定的。就用自定义代码分析来规范我们的开发人员。
自定义代码分析,就是让Visual Studio中的代码分析模块按照自定义的分析方式去分析公司程序写的代码,然后给出相应的Warnning(也可以是Error,根据设定来分类),就像每个方法的首字母都得是大写,如果是小写或非字母的话,就在错误列表中给出该方法一个Warnning(或Error)。
有了前面的背景,就进入正式的主题。
代码分析,本质就是把开发人员写的代码拿出来,看看那些不符合公司规范,然后把不符合的提示出来,让开发人员改。大家都知道refection,可以很好的把别人的代码检索出来。但有更好的一个技术,introspection,与refection功能一样,但它比refection更快,并且支持多线程,分析开发人员程序集时还不独占。看来很不错。我们只知道这么多就行了,因为我们不直接用introspection来做自定义代码分析,我们用BaseIntrospectionRule这个封装了introspection的基类来做代码分析,这样以来,会提升咱们的开发速度。BaseIntrospectionRule的类层次结构为:父类是StandardVisitor,再向上是Visitor(一个抽象类),再上就是Object了。
(以下我使用的是visual studio 2008)
首先创建一个CustomCodeAnalysisRules的解决方案,在这个解决方案下创建一个NameRuleCodeAnalysisRules类库项目。然后给这个项目添加引用,因为BaseIntrospectionRule这个类以及后面用的到的一些类都在将要引用的程程集下面。选中项目的“引用”右键“添加引用”,找到visual studio的安装目录(有可能你在安装时修改了安装目录)“D:\Program Files (x86)\Microsoft Visual Studio 9.0\Team Tools\Static Analysis Tools\FxCop”(这是我的目录),引两个dll进来,一个是FxCopSdk.dll,另一个是Microsoft.Cci.dll。
接下来,我们要对前面提到的两个规则进行编程,“方法首字母大写”和“字段不能是公有”
在项目中添加一个类文件
NameRuleCodeAnalysisRules
(为了方便,我们把方法验证和字段验证都放到一个类中)
using System;
using Microsoft.FxCop.Sdk;
namespace NameRuleCodeAnalysisRules
{
///
/// 验证方法类
///
public class ServiceSoftNameRuleMethod : BaseIntrospectionRule
{
public ServiceSoftNameRuleMethod()
: base( "ServiceSoftNameRuleMethod", "NameRuleCodeAnalysisRules.Rules", typeof(ServiceSoftNameRuleMethod).Assembly)
{ }
///
/// 验证方法首字母
///
/// "member">类型成员
///
public override ProblemCollection Check(Member member)
{
Method method = member as Method;
if (method == null)
{
return null;
}
else
{
if (!method.IsSpecialName && !Char.IsUpper(method.Name.Name, 0))
{
Problems.Add( new Problem(GetResolution(member.Name.Name)));
}
}
return Problems;
}
}
///
/// 验证字段类
///
public class ServiceSoftNameRuleField : BaseIntrospectionRule
{
public ServiceSoftNameRuleField()
: base( "ServiceSoftNameRuleField", "NameRuleCodeAnalysisRules.Rules", typeof(ServiceSoftNameRuleField).Assembly)
{ }
///
/// 验证字段访问修饰符
///
/// "member">类型成员
///
public override ProblemCollection Check(Member member)
{
if (member.DeclaringType is EnumNode)
{
return null;
}
Field field = member as Field;
if (field == null)
{
return null;
}
else
{
if (field.IsPublic)
{
Problems.Add( new Problem(GetResolution(member.Name.Name)));
}
return Problems;
}
}
}
}
using Microsoft.FxCop.Sdk;
namespace NameRuleCodeAnalysisRules
{
///
/// 验证方法类
///
public class ServiceSoftNameRuleMethod : BaseIntrospectionRule
{
public ServiceSoftNameRuleMethod()
: base( "ServiceSoftNameRuleMethod", "NameRuleCodeAnalysisRules.Rules", typeof(ServiceSoftNameRuleMethod).Assembly)
{ }
///
/// 验证方法首字母
///
/// "member">类型成员
///
public override ProblemCollection Check(Member member)
{
Method method = member as Method;
if (method == null)
{
return null;
}
else
{
if (!method.IsSpecialName && !Char.IsUpper(method.Name.Name, 0))
{
Problems.Add( new Problem(GetResolution(member.Name.Name)));
}
}
return Problems;
}
}
///
/// 验证字段类
///
public class ServiceSoftNameRuleField : BaseIntrospectionRule
{
public ServiceSoftNameRuleField()
: base( "ServiceSoftNameRuleField", "NameRuleCodeAnalysisRules.Rules", typeof(ServiceSoftNameRuleField).Assembly)
{ }
///
/// 验证字段访问修饰符
///
/// "member">类型成员
///
public override ProblemCollection Check(Member member)
{
if (member.DeclaringType is EnumNode)
{
return null;
}
Field field = member as Field;
if (field == null)
{
return null;
}
else
{
if (field.IsPublic)
{
Problems.Add( new Problem(GetResolution(member.Name.Name)));
}
return Problems;
}
}
}
}
其实
check
方法有多种重载,这里因为我们验证的方法和字段,都是类型成员所以只重写
check(Member member)
这种方法就可以了。在方法体中,如果类型成员不满足要求,
就以它名字为
Problem
构造参数的对象,添加到
Problems
中,最终,这个
Problem
对象的相应信息,会在错误列表中显示出来。这里要注意,
member.Name.Name
,也就是类型成员的名字。
C#
代写完成,要写一个每个规范类对应的
Rules.xml
xml
version
="1.0"
encoding
="utf-8"
?>
< Rules FriendlyName ="ServiceSoft公司命名规范" >
< Rule TypeName ="ServiceSoftNameRuleMethod" Category ="ServiceSoft.NameRule" CheckId ="SS0001" >
< Name >方法名规则 Name >
< Description >方法名必须首字母大写,如果首字符小写将提示警告。 Description >
< Url />
< Resolution >方法 “{0} ”的首字母应为大写 Resolution >
< MessageLevel Certainty ="99" >Error MessageLevel >
< Email >[email protected] Email >
< FixCategories >NonBreaking, DependsOnFix FixCategories >
< Owner >ServiceSoft Owner >
Rule >
< Rule TypeName ="ServiceSoftNameRuleField" Category ="ServiceSoft.NameRule" CheckId ="SS0002" >
< Name >字段名规则 Name >
< Description >字段的访问修饰符不能是公有的。 Description >
< Url />
< Resolution >字段 “{0} ”的访问修饰符是public Resolution >
< MessageLevel Certainty ="99" >Error MessageLevel >
< Email >[email protected] Email >
< FixCategories >NonBreaking, DependsOnFix FixCategories >
< Owner >ServiceSoft Owner >
Rule >
Rules >
< Rules FriendlyName ="ServiceSoft公司命名规范" >
< Rule TypeName ="ServiceSoftNameRuleMethod" Category ="ServiceSoft.NameRule" CheckId ="SS0001" >
< Name >方法名规则 Name >
< Description >方法名必须首字母大写,如果首字符小写将提示警告。 Description >
< Url />
< Resolution >方法 “{0} ”的首字母应为大写 Resolution >
< MessageLevel Certainty ="99" >Error MessageLevel >
< Email >[email protected] Email >
< FixCategories >NonBreaking, DependsOnFix FixCategories >
< Owner >ServiceSoft Owner >
Rule >
< Rule TypeName ="ServiceSoftNameRuleField" Category ="ServiceSoft.NameRule" CheckId ="SS0002" >
< Name >字段名规则 Name >
< Description >字段的访问修饰符不能是公有的。 Description >
< Url />
< Resolution >字段 “{0} ”的访问修饰符是public Resolution >
< MessageLevel Certainty ="99" >Error MessageLevel >
< Email >[email protected] Email >
< FixCategories >NonBreaking, DependsOnFix FixCategories >
< Owner >ServiceSoft Owner >
Rule >
Rules >
每个类对应一个
标记。最外面是
标记,这里要注意这个标记的
FriendlyName
这个属性值,最终会在图
-1
中的规则中显示出来。
在
中有如下子标记,
名称,
描述,
链接,
解决方案,这个标记会最终在错误列显示出来,所以对这个标记的内容要描述清楚,并且在这个标记的内容中有{
0
}这样的占位符,这是为前面方法中的
member.Name.Name
所占的。
信息级别,
Categories
为
0
~
99
范围的值,内容为
MessageLevel
枚举的值, 邮箱,
更正规则的方式,内容为
FixCategories
枚举的值,
所有者。
现在主可以生成类库集了,现在需要把
Rules.xml
一起生成到程序集的
dll
中,选中
Rules
右键“属性”,在属性的窗体中选中“生成操作”,下拉改成“嵌入的资源”,最终就会把这个
Rules.xml
打包到
dll
的元数据中。
再返回头来看一下规则类和
xml
,它们的关联是通过在规则类的构造函数中的第二个参数
NameRuleCodeAnalysisRules.Rules
,
namesapce+xml
名和
xml
的
的
Typ eName
属性为规则类名关联起来的。
复制上
NameRuleCodeAnalysisRules.dll
,到
visual studio
的安装目录
D:\Program Files (x86)\Microsoft Visual Studio 9.0\Team Tools\Static Analysis Tools\FxCop\Rules
下,这个目录下放的全是代码分析的规则类。
到此,为我们的开发规范自定义代码分析就做完了,好用吗?
新建一个控制台应用程序,名称自已决定
代码如下:
using System;
namespace ConsoleApplication1
{
class Program
{
public int i = 10;
static void Main( string[] args)
{
}
public void ff()
{
}
static void _gsw()
{
}
public int Sx
{ get; set; }
}
class abc
{
public string k= "12";
public abc()
{ }
}
abstract class bcd
{
public abstract int ffs();
}
}
namespace ConsoleApplication1
{
class Program
{
public int i = 10;
static void Main( string[] args)
{
}
public void ff()
{
}
static void _gsw()
{
}
public int Sx
{ get; set; }
}
class abc
{
public string k= "12";
public abc()
{ }
}
abstract class bcd
{
public abstract int ffs();
}
}
然后打开代码分析,如下图:
(图-2)
这时,就能看到我们自定议的规则“
ServiceSoft
公司命名规范”(这里的名称就是
Rules.xml
中的
FriendlyName
属性的值),为了测试明显,可以先把其他的规则关闭。生成如下图
(图-3)
说明中就会把我们自定议的规则以警告的形式提示出来。
有几个问题:
1、
当第一次代码分析时往往不进行,当源代码有一点改动,代码分析才起作用,我估计系统检测出源代码文件有变动,才去分析,这里应该用一点问题。
2、
还有所有的代码分析警告都不显示第几列。