我已经实现了代码检查,以验证过程是否具有通过引用隐式传递的参数-在VBA中,除非明确指定了ByRef,否则将传递ByVal的参数。这里是有问题的实现:

[ComVisible(false)]
public class ImplicitByRefParameterInspection : IInspection
{
    public ImplicitByRefParameterInspection()
    {
        Severity = CodeInspectionSeverity.Warning;
    }

    public string Name { get { return "Parameter is passed ByRef implicitly"; } }
    public CodeInspectionType InspectionType { get { return CodeInspectionType.CodeQualityIssues; } }
    public CodeInspectionSeverity Severity { get; set; }

    public IEnumerable<CodeInspectionResultBase> GetInspectionResults(SyntaxTreeNode node)
    {
        var procedures = node.FindAllProcedures().Where(procedure => procedure.Parameters.Any(parameter => !string.IsNullOrEmpty(parameter.Instruction.Value)));
        var targets = procedures.Where(procedure => procedure.Parameters.Any(parameter => parameter.IsImplicitByRef)
                                                && !procedure.Instruction.Line.IsMultiline);

        return targets.SelectMany(procedure => procedure.Parameters.Where(parameter => parameter.IsImplicitByRef)
            .Select(parameter => new ImplicitByRefParameterInspectionResult(Name, parameter.Instruction, Severity)));
    }
}


会有更好的方法吗?这是ImplicitByRefParameterInspectionResult类(真是个长名字!):

[ComVisible(false)]
public class ImplicitByRefParameterInspectionResult : CodeInspectionResultBase
{
    public ImplicitByRefParameterInspectionResult(string inspection, Instruction instruction, CodeInspectionSeverity type)
        : base(inspection, instruction, type)
    {
    }

    public override IDictionary<string, Action<VBE>> GetQuickFixes()
    {
        return !Handled
            ? new Dictionary<string, Action<VBE>>
                {
                    {"Pass parameter by value", PassParameterByVal},
                    {"Pass parameter by reference explicitly", PassParameterByRef}
                }
            : new Dictionary<string, Action<VBE>>();
    }

    private void PassParameterByRef(VBE vbe)
    {
        if (!Instruction.Line.IsMultiline)
        {
            var newContent = string.Concat(ReservedKeywords.ByRef, " ", Instruction.Value);
            var oldContent = Instruction.Line.Content;

            var result = oldContent.Replace(Instruction.Value, newContent);

            var module = vbe.FindCodeModules(Instruction.Line.ProjectName, Instruction.Line.ComponentName).First();
            module.ReplaceLine(Instruction.Line.StartLineNumber, result);
            Handled = true;
        }
        else
        {
            // todo: implement for multiline
            throw new NotImplementedException("This method is not [yet] implemented for multiline instructions.");
        }
    }

    private void PassParameterByVal(VBE vbe)
    {
        if (!Instruction.Line.IsMultiline)
        {
            var newContent = string.Concat(ReservedKeywords.ByVal, " ", Instruction.Value);
            var oldContent = Instruction.Line.Content;

            var result = oldContent.Replace(Instruction.Value, newContent);

            var module = vbe.FindCodeModules(Instruction.Line.ProjectName, Instruction.Line.ComponentName).First();
            module.ReplaceLine(Instruction.Line.StartLineNumber, result);
            Handled = true;
        }
        else
        {
            // todo: implement for multiline
            throw new NotImplementedException("This method is not yet implemented for multiline instructions.");
        }
    }
}


欢迎任何评论。

评论

检查是否在您的控制之下?并且是代码检查的严重程度{get;组; }接口的一部分?

@TopinFrassi是的,是的:)

#1 楼

从此开始

var procedures = node.FindAllProcedures().Where(procedure => procedure.Parameters.Any(parameter => !string.IsNullOrEmpty(parameter.Instruction.Value)));
var targets = procedures.Where(procedure => procedure.Parameters.Any(parameter => parameter.IsImplicitByRef)
                                        && !procedure.Instruction.Line.IsMultiline);

return targets.SelectMany(procedure => procedure.Parameters.Where(parameter => parameter.IsImplicitByRef)
    .Select(parameter => new ImplicitByRefParameterInspectionResult(Name, parameter.Instruction, Severity)));


将其折叠为一个表达式

return node.FindAllProcedures()
    .Where(procedure => procedure.Parameters.Any(parameter => !string.IsNullOrEmpty(parameter.Instruction.Value)))
    .Where(procedure => procedure.Parameters.Any(parameter => parameter.IsImplicitByRef)
                                        && !procedure.Instruction.Line.IsMultiline)
    .SelectMany(procedure => procedure.Parameters.Where(parameter => parameter.IsImplicitByRef)
    .Select(parameter => new ImplicitByRefParameterInspectionResult(Name, parameter.Instruction, Severity)));


将最后一个Select移出SelectMany

return node.FindAllProcedures()
    .Where(procedure => procedure.Parameters.Any(parameter => !string.IsNullOrEmpty(parameter.Instruction.Value)))
    .Where(procedure => procedure.Parameters.Any(parameter => parameter.IsImplicitByRef)
                                        && !procedure.Instruction.Line.IsMultiline)
    .SelectMany(procedure => procedure.Parameters)
    .Where(parameter => parameter.IsImplicitByRef)
    .Select(parameter => new ImplicitByRefParameterInspectionResult(Name, parameter.Instruction, Severity));


现在看来procedure.Parameters.Any(parameter => parameter.IsImplicitByRef)是多余的,所以让我们删除它并加入后续的Where s

return node.FindAllProcedures()
    .Where(procedure => procedure.Parameters.Any(parameter => !string.IsNullOrEmpty(parameter.Instruction.Value))
                        && !procedure.Instruction.Line.IsMultiline)
    .SelectMany(procedure => procedure.Parameters)
    .Where(parameter => parameter.IsImplicitByRef)
    .Select(parameter => new ImplicitByRefParameterInspectionResult(Name, parameter.Instruction, Severity));


尚不清楚为什么需要procedure.Parameters.Any(parameter => !string.IsNullOrEmpty(parameter.Instruction.Value)) -如果需要,则需要注释,否则我们可以只写

return node.FindAllProcedures()
    .Where(procedure => !procedure.Instruction.Line.IsMultiline)
    .SelectMany(procedure => procedure.Parameters)
    .Where(parameter => parameter.IsImplicitByRef)
    .Select(parameter => new ImplicitByRefParameterInspectionResult(Name, parameter.Instruction, Severity));


哪个看起来更易于管理。或者,如果您希望使用其他语法

return from procedure in node.FindAllProcedures()
       where !procedure.Instruction.Line.IsMultiline
       from parameter in procedure.Parameters
       where parameter.IsImplicitByRef
       select new ImplicitByRefParameterInspectionResult(Name, parameter.Instruction, Severity);


#2 楼

我不知道我对这三元的感觉。


public override IDictionary<string, Action<VBE>> GetQuickFixes()
{
    return !Handled
        ? new Dictionary<string, Action<VBE>>
            {
                {"Pass parameter by value", PassParameterByVal},
                {"Pass parameter by reference explicitly", PassParameterByRef}
            }
        : new Dictionary<string, Action<VBE>>();
}



我的意思是,这的确可能会更糟,而且我承认除了非常简单的任务外,我不喜欢三元组。我觉得这里需要一个if语句。尽管它将添加几行代码,但我认为它将增加可读性。另一方面,不是因为混乱,而是因为。做得好。我不讨厌(我是否提到我讨厌三元组?)

我将如何编写它。

public override IDictionary<string, Action<VBE>> GetQuickFixes()
{
    var result = new Dictionary<string, Action<VBE>>;

    if (!Handled)
    {
        result.Add("Pass parameter by value", PassParameterByVal);
        result.Add("Pass parameter by reference explicitly", PassParameterByRef);
    }

    return result;
}


每种方法都有其优缺点。我的方法降低了缩进的级别,但是引入了一个中间变量


Hmm ..在每个实现中三元都存在.. – Mat的杯子


我认为基类中的GetQuickFixes应该以字典作为参数。然后,不必在每个实现中都使用此逻辑。它只需要为未处理的案例创建适当的字典,将其传递给base,然后返回base认为必要的任何字典。这样,这种逻辑就不需要一遍又一遍地实现。您应该最终得到如下所示的内容。

public override IDictionary<string, Action<VBE>> GetQuickFixes()
{
    var fixes = new Dictionary<string, Action<VBE>>
         {
             {"Pass parameter by value", PassParameterByVal},
             {"Pass parameter by reference explicitly", PassParameterByRef}
         } 
    return base.GetQuickFixes(fixes);
}


现在三元数在基类中变得可以接受。

public IDictionary<string, Action<VBE>> GetQuickFixes(IDictionary<string, Action<VBE>> actions)
{
    return !Handled ? actions : new Dictionary<string, Action<VBE>>();
}


评论


\ $ \ begingroup \ $
嗯..每种实现中都有三元组。
\ $ \ endgroup \ $
– Mathieu Guindon♦
2014年12月18日,0:25

\ $ \ begingroup \ $
老实说,还不错。只是一个小问题,我相信对于每个同意我的人,都会有两个不同意。
\ $ \ endgroup \ $
–RubberDuck
2014年12月18日下午0:26

\ $ \ begingroup \ $
我个人会重新排列三元组,但将其保留为三元组,因为您知道这是一把锤子。
\ $ \ endgroup \ $
–马拉奇♦
2014年12月18日下午4:16

\ $ \ begingroup \ $
我喜欢这个它仅创建一次返回值。
\ $ \ endgroup \ $
– Mathieu Guindon♦
2014年12月18日下午4:27

\ $ \ begingroup \ $
好的。我认为上次编辑。有另一个想法。让我感到困扰的是,每个实现中都有相同的逻辑。
\ $ \ endgroup \ $
–RubberDuck
2014年12月18日下午4:59

#3 楼


public override IDictionary<string, Action<VBE>> GetQuickFixes()
{
    return !Handled
        ? new Dictionary<string, Action<VBE>>
            {
                {"Pass parameter by value", PassParameterByVal},
                {"Pass parameter by reference explicitly", PassParameterByRef}
            }
        : new Dictionary<string, Action<VBE>>();
}



这样我就可以解决这个问题,以便我可以使用Handled而不是
不处理...

所以看起来像这样

public override IDictionary<string, Action<VBE>> GetQuickFixes()
{
    return Handled
        ? new Dictionary<string, Action<VBE>>()
        : new Dictionary<string, Action<VBE>>
            {
                {"Pass parameter by value", PassParameterByVal},
                {"Pass parameter by reference explicitly", PassParameterByRef}
            };
}


#4 楼


var newContent = string.Concat(ReservedKeywords.ByVal, " ", Instruction.Value);



这是我最近学到的一个很妙的东西:

var newContent = ReservedKeywords.ByVal + " " + Instruction.Value;


将被编译器优化为致电string.Concat,与上述完全相同。它也更具可读性,因此您不会有任何损失。您可以在Eric Lippert的博客上了解有关它的更多信息。


方法PassParameterByRefPassParameterByVal相同,除了一个变量外,并且可以(除非您打算更改它们)常见的方法。

#5 楼

我不确定Severitypublic set的事实。作为您代码的客户端,我是谁说ImplicitByRefParameterInspection的严重性为X,而您作为了解他的知识的开发人员说它的严重性为Warning

我认为您界面应该只暴露CodeInspectionSeverity Severity上的吸气剂。

我不是项目的一部分,但是我很难看清要set的严重性的情况。由于它是一种抽象,因此您无法确定它的严重性。

(我认为)您想设置IInspection的实现的严重性。

如果我将其转换为代码,如下所示:

public interface IInspection
{
    CodeInspectionSeverity Severity { get; }
}

[ComVisible(false)]
public class ImplicitByRefParameterInspection : IInspection
{
    public ImplicitByRefParameterInspection()
    {
        Severity = CodeInspectionSeverity.Warning;
    }

    public CodeInspectionSeverity Severity { get; private set; }
}


这样,我(作为客户)可以知道检查的严重性,但是我无法IInspection,这是一件好事,因为我完全不知道应该是什么。

评论


\ $ \ begingroup \ $
我应该在该帖子中包含该枚举。.它从DoNotShow变为Error-打算由使用它的开发人员进行配置-如果您想将检查类型显示为“提示”,则为“建议”或“警告”,或者如果您不想看到它,则可以进行配置。用户(VBA / COM)没有看到该类型,因此无法以编程方式访问它。
\ $ \ endgroup \ $
– Mathieu Guindon♦
2014年12月18日15:30

\ $ \ begingroup \ $
+1因为您的回答在上下文中是有意义的,但实际上有一种配置可以让用户指定严重性级别。]
\ $ \ endgroup \ $
–RubberDuck
2014-12-18 15:30



\ $ \ begingroup \ $
目前还不确定我是否写了答案,但是我认为它仍然很有用! :)
\ $ \ endgroup \ $
–IEatBagels
2014年12月18日15:33