The Microsoft source analyzer for C#, StyleCop can be used
to enforce a set of styling and consistency rules among .Net teams in a
project/ company. StyleCop can be run as a visual studio plugin or can be
integrated with an MSBuild project.
StyleCop provides an extensible framework for plugging in
custom rules for the developers. For
implementing a custom rule, the user needs to create a custom rules analyzer
class which inherits the SourceAnalyzer and overrides the AnalyzeDocument
method to check for violations.
In this post, I’ll show how you can implement extensible custom
rules in stylecop by using the visitor pattern.
Creating the custom rule analyzer class:
[SourceAnalyzer(typeof(CsParser))]
public class MyCodeStandardAnalyzerRules : SourceAnalyzer
{
public override void AnalyzeDocument(CodeDocument document)
{
var csDoc = document as CsDocument;
if(csDoc == null) return;
if(csDoc.HasEmptyRootElement() ||
csDoc.IsAutoGeneratedCode()) return;
csDoc.WalkDocument(
ElementVisitor, null, null
);
}
private bool ElementVisitor(CsElement element, CsElement parentelement, object context)
{
//Implementing
the custom rules for CsElement goes here
}
}
Creating the visitor interface
public interface IVisitor
{
void SetSourceAnalyzer(SourceAnalyzer alanyzer);
void Visit(CsElement element);
}
Writing the first visitor
public class CheckForConstantsHaveUpperCaseNameVisitor : BaseVisitor, IVisitor
{
public void Visit(CsElement element)
{
if (element.ElementType != ElementType.Field || !element.Declaration.ContainsModifier(CsTokenType.Const))
return;
if(element.Name.Any(char.IsLower))
AddViolation(element, RuleNames.CONSTANTS_SHOULD_BE_IN_UPPERCASE,
element.Name, element.LineNumber);
}
}
public class BaseVisitor
{
private SourceAnalyzer _sourceAnalyzer;
public void SetSourceAnalyzer(SourceAnalyzer sourceAnalyzer)
{
if (sourceAnalyzer == null) throw new ArgumentNullException("sourceAnalyzer");
_sourceAnalyzer = sourceAnalyzer;
}
protected void AddViolation(ICodeElement element, string rule, params object[] values)
{
_sourceAnalyzer.AddViolation(element,
rule, values);
}
}
Creating the Element class to accept the visitor
public class CodeElement
{
private readonly SourceAnalyzer _analyzer;
private readonly CsElement _element;
public CodeElement(SourceAnalyzer analyzer, CsElement element)
{
_analyzer = analyzer;
_element = element;
}
public void Accept(IVisitor visitor)
{
visitor.SetSourceAnalyzer(_analyzer);
visitor.Visit(_element);
}
}
The visitor dispatcher to resolve all visitors (Using a dependency
injection container is much easier J)
public class VisitorDispatcher
{
private static VisitorDispatcher _dispatcher;
private static readonly object _lockObject = new object();
public List<IVisitor> Visitors { get; private set; }
public static VisitorDispatcher Instance
{
get
{
if (_dispatcher == null)
{
lock (_lockObject)
{
_dispatcher = new VisitorDispatcher();
var visitors = from type in Assembly.GetExecutingAssembly().GetTypes()
where type.IsAssignableFrom(typeof(IVisitor))
select Activator.CreateInstance(type)
as IVisitor;
_dispatcher.Visitors = new List<IVisitor>(visitors);
}
}
return _dispatcher;
}
}
}
Finally visiting the element with the visitors
private bool ElementVisitor(CsElement element, CsElement parentelement, object context)
{
if (element.IsAutoGenerated()) return true;
var codeElement = new CodeElement(this, element);
VisitorDispatcher.Instance.Visitors.ForEach(codeElement.Accept);
return true;
}
The last step is to build your project and drop the new
project’s dll into the StyleCop installation directory and run the style cop
rules on your projects. Style cop will automatically look for assemblies in the
directory and pick up the new rules for your team.
No comments:
Post a Comment