• 编写StyleCop自定义规则教程(一)编写中文备注的简单校验规则


    基本步骤:

    1、 新建Dll项目,引用StyleCop.DllStyleCop.Csharp.Dll

    2、 自定义的规则校验类从SourceAnalyzer继承,必须实现public virtual void AnalyzeDocument(CodeDocument document)方法。

    3、 在需要触发违反规则的地方使用方法AddViolation

    4、 在项目中增加一个xml嵌入资源,该资源的文件名必须与校验类同名。该xml可以定义校验类显示在StyleCop Setting的规则、规则描述和参数等。

    5、 编译成功,将Dll复制到StyleCopy的目录下。

        StyleCopDocumnetaion Rules对代码的文档有一系列的要求,譬如SA1623 要求属性的备注文档开头必须注明whether the property exposes get or set accessors。其校验的实现方式是在备注的开头判断是否有属性访问权对应的Get/ Set字符。中文用户是不大适应用GetSet作备注的,因此以下一个简单例子实现一个判断中文备注的规则,代替原有的SA1623

         由于每个Rule都必须有唯一的RuleNameRuleIDRuleName只是用于内部,不被用户看见。RuleID2位大写字母开头和4位数字组成。SA1623就是RuleID。例子使用MY1623StyleCop原有的RuleID相对。

         覆盖AnalyzeDocument方法

    1 public override void AnalyzeDocument(CodeDocument document)
    2         {
    3             Param.RequireNotNull(document, "document");
    4             CsDocument document2 = (CsDocument)document;
    5             if ((document2.RootElement != null&& !document2.RootElement.Generated)
    6             {
    7                 document2.WalkDocument(new CodeWalkerElementVisitor<object>(this.CheckDocumentationForElement), null);
    8             }
    9         }

    WalkDocumnet方法是遍历代码中的各个节点(方法、属性等)。CheckDocumentationForElementelement.ElementType == ElementType.Property是执行具体操作的方法。这里编写的规则只针对属性。


    private bool CheckDocumentationForElement(CsElement element, CsElement parentElement, Object settings)
            {
                
    if (base.Cancel)
                {
                    
    return false;
                }

                
    if (!element.Generated)
                {
                    
    if (element.ElementType == ElementType.Property)
                    {
                        
    this.ParseHeader(element, element.Header, element.LineNumber, false);
                    }
                }
                
    return true;
            }
    private readonly string headerSummaryForGetAndSetAccessor = "可读写";
            
    private readonly string headerSummaryForGetAccessor = "只读";
            
    private readonly string headerSummaryForSetAccessor = "只写";
            
            
    private void ParseHeader(CsElement element, XmlHeader header, int lineNumber, bool partialElement)
            {
                XmlDocument doc 
    = this.LoadHeaderIntoDocument(element, header, lineNumber);
                
    if ((doc != null&& (element.ElementType == ElementType.Property))
                {
                    
    this.CheckPropertySummaryFormatting(element as Property, doc);
                }
            }

            
    private XmlDocument LoadHeaderIntoDocument(CsElement element, XmlHeader header, int lineNumber)
            {
                XmlDocument document 
    = new XmlDocument();
                
    try
                {
                    
    string xml = "<root>" + header.Text + "</root>";
                    document.LoadXml(xml);
                }
                
    catch (XmlException exception)
                {
                    
    base.AddViolation(element, lineNumber, Rules.DocumentationMustContainValidXml, new object[] { exception.Message });
                    document 
    = null;
                }
                
    catch (Exception exception)
                {
                    
    base.AddViolation(element, lineNumber, Rules.DocumentationMustContainValidXml, new object[] { exception.Message });
                    document 
    = null;
                }
                
    return document;
            }
            
            
    private void CheckPropertySummaryFormatting(Property property, XmlDocument doc)
            {
                XmlNode node 
    = doc.SelectSingleNode("root/summary");
                
    if (node != null)
                {
                    
    bool flag = ((property.ReturnType.Text == "bool"|| (property.ReturnType.Text == "Boolean")) || (property.ReturnType.Text == "System.Boolean");
                    
    if (property.GetAccessor != null)
                    {
                        
    if ((property.SetAccessor == null|| ((property.SetAccessor.AccessModifier != property.AccessModifier) && (((property.AccessModifier == AccessModifierType.Private) || (property.SetAccessor.AccessModifier != AccessModifierType.Private)) || property.SetAccessor.Declaration.ContainsModifier(new CsTokenType[] { CsTokenType.Private }))))
                        {
                            
    string str2 = headerSummaryForGetAccessor;
                            
    string str3 = node.InnerText.TrimStart(new char[0]);                        
                            
    if (!CompareTextWithColon(str3, str2))
                            {
                                
    base.AddViolation(property, Rules.PropertySummaryDocumentationMustMatchAccessors, new object[] { str2 });
                            }
                        }
                        
    else
                        {
                            
    string str = headerSummaryForGetAndSetAccessor;                        
                            
    if (!CompareTextWithColon(node.InnerText.TrimStart(new char[0]), str))
                            {
                                
    base.AddViolation(property, Rules.PropertySummaryDocumentationMustMatchAccessors, new object[] { str });
                            }
                        }
                    }
                    
    else if (property.SetAccessor != null)
                    {
                        
    string str5 = headerSummaryForSetAccessor;                   
                        
    if (!CompareTextWithColon(node.InnerText.TrimStart(new char[0]), str5))
                        {
                            
    base.AddViolation(property, Rules.PropertySummaryDocumentationMustMatchAccessors, new object[] { str5 });
                        }
                    }
                }
            }

            
    private bool CompareTextWithColon(string input, string pattern)
            {
                
    return Regex.IsMatch(input, pattern + "[::]", RegexOptions.Singleline);
            }

    对应的资源xml

    View Code
    <?xml version="1.0" encoding="utf-8" ?>
    <SourceAnalyzer Name="My Documentation Rules">
      
    <Description>
        Rules which verify the content and formatting of code documentation.
      
    </Description>  
      
    <Rules>      
          
    <Rule Name="DocumentationMustContainValidXml" CheckId="HD1603">
            
    <Context>The documentation header is composed of invalid Xml: {0}</Context>
            
    <Description>Indicates that a documentation header is composed of invalid Xml and cannot be parsed.</Description>
          
    </Rule>      
          
    <Rule Name="PropertySummaryDocumentationMustMatchAccessors" CheckId="HD1623">
            
    <Context>属性的摘要文档说明必须使用“{0}”开头</Context>
            
    <Description>Validates that a property's summary description text begins with the correct syntax, depending upon whether the property exposes get or set accessors.</Description>
          
    </Rule>
      
    </Rules>
    </SourceAnalyzer>

     其中Description节点的内容是显示在Stylecop Setting窗口下方的文字。Context是违反规则时,显示在VS 错误列表的文字。

    附上代码:/Files/Byeah/MyFirstRule.zip

    下一篇:《编写StyleCop自定义规则教程(二)---校验规则增加配置参数 》

  • 相关阅读:
    mysql 主从服务器配置
    Linux命令
    Kali
    Python进阶
    性能测试工具
    sphinx搜索
    页面静态化
    PHP API接口
    线程的生命周期
    多线程的创建
  • 原文地址:https://www.cnblogs.com/Byeah/p/2091222.html
Copyright © 2020-2023  润新知