基於幾篇很好的文章,我已經能夠成功創建幾個自定義StyleCop規則。作爲參考,幾篇文章,我發現關於這個主題非常有用的是這裏列出:如何在StyleCop設置和併發中的單個「自定義規則」節點下合併多個自定義StyleCop規則
- How to Implement a Custom StyleCop Rule
- Creating Custom Rules for Microsoft Source Analyzer - Part I
- Creating Custom Rules for Microsoft Source Analyzer - Part II
- Creating Custom Rules for Microsoft Source Analyzer - Part III
我使用Visual Studio 2010終極版以及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被執行。
我該如何解決這個問題?
更新:基於公認的答案
,我參加了一個單人分析儀內檢查我所有的自定義規則的做法。
從我的理解來看,每個表達樹walker運行在它自己的線程上,因此狀態在這個過程中不能被輕易地共享。如果我使用單個分析儀的方法,我可以安全地執行以下操作嗎?
[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
}
}
}
更新:基於
進一步測試上方是不安全。不能使用實例字段來維護狀態。
當有多個源代碼文件的項目運行了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;
}
}
感謝您的意見。請參閱我的更新。在調用doc.WalkDocument(...)時,維護哪些規則正在檢查的狀態是否安全? – Elan 2011-05-15 22:23:20
@Elan:StyleCop分析器提供了豐富多樣的方法,可以讓你用數百種方式編寫分析器代碼。在你的特定模式中,似乎你不需要currentRule字段。只需從VisitElement()方法開始走路並引發適當的錯誤。 – 2011-05-16 08:29:06
謝謝,我很好地使用了一臺分析儀,這很合理。如果我共享相同的VistElement()方法,那麼我必須區分正在運行的規則,以便檢查適用於正在驗證的規則。如果我爲每個規則創建單獨的VisitElement()回調,那麼很自然我就不需要currentRule字段。 – Elan 2011-05-16 15:25:20