基于几篇好文章,我已经能够成功创建一些自定义StyleCop规则。作为参考,我在这里列出了一些我发现对这个主题非常有用的文章:
我正在使用Visual Studio 2010 Ultimate版以及StyleCop版本4.4.0.14。
创建自定义StyleCop规则会调用创建类文件以及相应的XML文件,该文件用于将规则添加到StyleCop设置。当我这样做时,我的所有自定义规则都会正确执行。但是,我不喜欢的是,在StyleCop设置树中,您最终会获得多个“自定义规则”节点,每个节点对应一个XML文件。
跳过不同规则的实现细节,这就是我所做的。让我们沿着相应的XML文件获取以下两个简单的自定义规则类:
文件:CustomRule1.cs
namespace StyleCop.CustomRules
{
[SourceAnalyzer(typeof(CsParser))]
public class CustomRule1 : SourceAnalyzer
{
public override void AnalyzeDocument(CodeDocument document)
{
Param.RequireNotNull(document, "document");
CsDocument csDocument = document as CsDocument;
if ((csDocument.RootElement != null) && !csDocument.RootElement.Generated)
{
// Do something...
}
}
}
}
文件:CustomRule2.cs
namespace StyleCop.CustomRules
{
[SourceAnalyzer(typeof(CsParser))]
public class CustomRule2 : SourceAnalyzer
{
public override void AnalyzeDocument(CodeDocument document)
{
Param.RequireNotNull(document, "document");
CsDocument csDocument = document as CsDocument;
if ((csDocument.RootElement != null) && !csDocument.RootElement.Generated)
{
// Do something...
}
}
}
}
文件:CustomRule1.xml
<?xml version="1.0" encoding="utf-8" ?>
<SourceAnalyzer Name="Custom Rules">
<Description>
These custom rules provide extensions to the ones provided with StyleCop.
</Description>
<Rules>
<Rule Name="CustomRule1" CheckId="CR1001">
<Context>Test rule 1.</Context>
<Description>Test rule 1.</Description>
</Rule>
</Rules>
</SourceAnalyzer>
文件:CustomRule2.xml
<?xml version="1.0" encoding="utf-8" ?>
<SourceAnalyzer Name="Custom Rules">
<Description>
These custom rules provide extensions to the ones provided with StyleCop.
</Description>
<Rules>
<Rule Name="CustomRule2" CheckId="CR1002">
<Context>Test rule 2.</Context>
<Description>Test rule 2.</Description>
</Rule>
</Rules>
</SourceAnalyzer>
通过上述内容,我的所有规则(两个)都得到了正确执行。 StyleCop设置树中出现以下内容(方括号代表一个复选框):
[] C#
[] {} Custom Rules
[] {} CR1001: CustomRule1
[] {} Custom Rules
[] {} CR1002: CustomRule2
[] {} Documentation Rules
[] {} Layout Rules
etc.
我想要的是将我的自定义规则放在StyleCop设置文件中名为“自定义规则”的一个节点下,如下所示:
[] C#
[] {} Custom Rules
[] {} CR1001: CustomRule1
[] {} CR1002: CustomRule2
[] {} Documentation Rules
[] {} Layout Rules
etc.
通过将两个XML文件合并为一个,我可以将规则组合到StyleCop设置中的单个“自定义规则”节点中,如下所示:
文件:CustomRule1.xml
<?xml version="1.0" encoding="utf-8" ?>
<SourceAnalyzer Name="Custom Rules">
<Description>
These custom rules provide extensions to the ones provided with StyleCop.
</Description>
<Rules>
<Rule Name="CustomRule1" CheckId="CR1001">
<Context>Test rule 1.</Context>
<Description>Test rule 1.</Description>
</Rule>
<Rule Name="CustomRule2" CheckId="CR1002">
<Context>Test rule 2.</Context>
<Description>Test rule 2.</Description>
</Rule>
</Rules>
</SourceAnalyzer>
但是,一旦我这样做,只执行了一个自定义规则,那就是 CustomRule1 ,即类(文件)名称与XML文件名匹配的规则。
我尝试在 CustomRule2 上设置属性以指示XML文件,如下所示:
namespace StyleCop.CustomRules
{
[SourceAnalyzer(typeof(CsParser), "CustomRule1.xml")]
public class CustomRule2 : SourceAnalyzer
{
public override void AnalyzeDocument(CodeDocument document)
{
Param.RequireNotNull(document, "document");
CsDocument csDocument = document as CsDocument;
if ((csDocument.RootElement != null) && !csDocument.RootElement.Generated)
{
// Do nothing.
}
}
}
}
如上所示将属性设置为XML文件也无法解决此问题。这两个规则都出现在StyleCop设置中,但只执行 CustomRule1 。
我该如何解决这个问题?
更新
根据接受的答案,我采用了在单个分析仪中检查所有自定义规则的方法。
根据我的理解,每个表达式树步行器都在自己的线程上运行,因此在此过程中无法轻松共享状态。如果我采用单一分析仪的方法,我可以安全地执行以下操作吗?
[SourceAnalyzer(typeof(CsParser))]
public class CustomRules : SourceAnalyzer
{
private enum CustomRuleName
{
CustomRule1,
CustomRule2
}
private CustomRuleName currentRule;
public override void AnalyzeDocument(CodeDocument document)
{
Param.RequireNotNull(document, "document");
CsDocument doc = document as CsDocument;
// Do not analyze empty documents, code generated files and files that
// are to be skipped.
if (doc.RootElement == null || doc.RootElement.Generated)
{
return;
}
// Check Rule: CustomRule1
this.currentRule = CustomRuleName.CustomRule1;
doc.WalkDocument(VisitElement);
// Check Rule: CustomRule2
this.currentRule = CustomRuleName.CustomRule2;
doc.WalkDocument(VisitElement);
}
private bool VisitElement(CsElement element, CsElement parentElement, object context)
{
if (this.currentRule == CustomRuleName.CustomRule1)
{
// Do checks only applicable to custom rule #1
}
else if (this.currentRule == CustomRuleName.CustomRule2)
{
// Do checks only applicable to custom rule #2
}
}
}
更新
根据进一步测试,上述 NOT 安全。无法使用实例字段来维护状态。
在具有多个源代码文件的项目上运行StyleCop时,多个线程将共享同一个分析器实例。
此外,鉴于以下代码,当调用doc.WalkDocument(...)
方法时,多个线程和并发性也会在正在分析的每个源代码文档上发挥作用。每个表达式树walker都在自己的线程上运行。
换句话说,除了可以在多个线程上同时分析多个源代码文件这一事实之外,回调VisitElement
,StatementWalker
和ExpressionWalker
也在不同的线程上执行
[SourceAnalyzer(typeof(CsParser))]
public class CustomRules : SourceAnalyzer
{
public override void AnalyzeDocument(CodeDocument document)
{
Param.RequireNotNull(document, "document");
CsDocument doc = document as CsDocument;
// Do not analyze empty documents, code generated files and files that
// are to be skipped.
if (doc.RootElement == null || doc.RootElement.Generated)
{
return;
}
IDictionary<string, Field> fields = new Dictionary<string, Field>();
doc.WalkDocument(VisitElement, StatementWalker, ExpressionWalker, fields);
}
private bool VisitElement(CsElement element, CsElement parentElement, object context)
{
// Do something...
return true;
}
private bool StatementWalker(Statement statement, Expression parentExpression, Statement parentStatement, CsElement parentElement, object context)
{
// Do something...
return true;
}
private bool ExpressionWalker(Expression expression, Expression parentExpression, Statement parentStatement, CsElement parentElement, object context)
{
// Do something...
return true;
}
}
答案 0 :(得分:3)
通常分析器包含多个规则(否则会很奇怪)。 每个分析器在设置UI中显示为单独的节点。
如果您想在设置界面中使用单个节点,您肯定只需要一台分析仪,这将执行两项检查。
namespace StyleCop.CustomRules
{
[SourceAnalyzer(typeof(CsParser))]
public class MyCustomRules : SourceAnalyzer
{
public override void AnalyzeDocument(CodeDocument document)
{
// ...
// code here can raise CR1001 as well as CR1002
}
}
}
文件:MyCustomRules.xml
<?xml version="1.0" encoding="utf-8" ?>
<SourceAnalyzer Name="My Custom Rules">
<Description>
These custom rules provide extensions to the ones provided with StyleCop.
</Description>
<Rules>
<Rule Name="CustomRule1" CheckId="CR1001">
<Context>Test rule 1.</Context>
<Description>Test rule 1.</Description>
</Rule>
<Rule Name="CustomRule2" CheckId="CR1002">
<Context>Test rule 2.</Context>
<Description>Test rule 2.</Description>
</Rule>
</Rules>
</SourceAnalyzer>
答案 1 :(得分:2)
如果某人对此主题感兴趣,我已制定了一些自定义规则,并且我能够对这些规则进行分组。事实上,如果有人对将参数传递给警告消息感兴趣,我的规则也会这样做:)。
规则:1。NamespaceMustBeginWithAllowedCompanyName 2. FieldNamesMustBeginWithUnderscore
文件名:StyleCopExtensions.cs
private bool VisitElement(CsElement element, CsElement parentElement, object context)
{
#region Namespace rules
if (!element.Generated && element.ElementType == ElementType.Namespace)
{
var @namespace = element.Declaration.Name;
var companyName = NamespaceUtils.GetNamespaceToken(@namespace, NamespaceTokenType.Company);
//Flag a "Violation" is the element doesn't start with an allowed company name
if (!NamespaceUtils.ValidateNamespaceCompany(companyName))
{
AddViolation(parentElement, element.LineNumber, "NamespaceMustBeginWithAllowedCompanyName", companyName);
}
#endregion
#region Fields rules
if (!element.Generated && element.ElementType == ElementType.Field)
{
var fieldName = element.Declaration.Name;
// Flag a violation if the instance variables are not prefixed with an underscore.
if (element.ActualAccess != AccessModifierType.Public &&
element.ActualAccess != AccessModifierType.Internal &&
fieldName.ToCharArray()[0] != '_')
{
AddViolation(parentElement, element.LineNumber, "FieldNamesMustBeginWithUnderscore", fieldName);
}
}
#endregion
return true;
}
Xml filename:StyleCopExtensions.xml - 与下面发布的其他xml文件类似。 - 您可以使用与'string.Format()'相同的方式使用消息中发送的参数:只需在xml文件中包含{0},{1},{N}。