如何在StyleCop设置和并发中的单个“自定义规则”节点下组合多个自定义StyleCop规则

基于几篇好文章,我已经能够成功创建一些自定义StyleCop规则。 作为参考,我在这里列出了一些我发现对这个主题非常有用的文章:

  • 如何实现自定义StyleCop规则
  • 为Microsoft Source Analyzer创建自定义规则 – 第一部分
  • 为Microsoft Source Analyzer创建自定义规则 – 第二部分
  • 为Microsoft Source Analyzer创建自定义规则 – 第III部分

我正在使用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

    These custom rules provide extensions to the ones provided with StyleCop.    Test rule 1. Test rule 1.    

文件:CustomRule2.xml

    These custom rules provide extensions to the ones provided with StyleCop.    Test rule 2. Test rule 2.    

有了上述内容,所有(两个)我的规则都得到了正确执行。 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

    These custom rules provide extensions to the ones provided with StyleCop.    Test rule 1. Test rule 1.   Test rule 2. Test rule 2.    

但是,一旦我这样做,只执行了一个自定义规则,而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 } } } 

更新:

根据进一步测试,上述情况并不安全。 无法使用实例字段来维护状态。

  1. 在具有多个源代码文件的项目上运行StyleCop时,多个线程将共享同一个分析器实例。

  2. 此外,给定下面的代码,当调用doc.WalkDocument(...)方法时,多个线程和并发性也会在正在分析的每个源代码文档上发挥作用。 每个表达式树walker都在自己的线程上运行。

换句话说,除了可以在多个线程上同时分析多个源代码文件这一事实之外,回调VisitElementStatementWalkerExpressionWalker也在不同的线程上执行。

 [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 fields = new Dictionary(); 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; } } 

通常分析器包含多个规则(否则会很奇怪)。 每个分析器在设置UI中显示为单独的节点。

如果您想在设置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

    These custom rules provide extensions to the ones provided with StyleCop.    Test rule 1. Test rule 1.   Test rule 2. Test rule 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文件名:StyleCopExtensions.xml – 与下面发布的其他xml文件类似。 – 你可以像’string.Format()’那样使用消息中发送的参数:只需在xml文件中包含{0},{1},{N}。