SonarLint - 关于 VB.NET 的一些规则的问题
SonarLint - questions about some of the rules for VB.NET
我在 Java 中遇到的绝大多数 SonarLint 规则似乎都是合理且合理的。然而,自从我开始为 VB.NET 使用 SonarLint 以来,我遇到了一些规则,这些规则让我质疑它们的用处,甚至质疑它们是否正常工作。
我想知道这是否只是我以次优方式使用某些 VB.NET 构造的问题,或者规则是否真的存在缺陷。
(抱歉,如果这个问题有点长。我不知道是否应该为每个单独的规则创建一个单独的问题。)
我发现以下规则会留下一些未考虑的情况,这些情况实际上会变成误报:
S1871:同一个条件结构中的两个分支不应该有完全相同的实现
我发现这个为我带来了很多误报,因为有时检查条件的顺序确实很重要。以下列伪代码为例:
If conditionA() Then
doSomething()
ElseIf conditionB() AndAlso conditionC() Then
doSomethingElse()
ElseIf conditionD() OrElse conditionE() Then
doYetAnotherThing()
'... feel free to have even more cases in between here
Else Then
doSomething() 'Non-compliant
End If
如果我想遵循此 Sonar 规则并仍然使代码以相同的方式运行,我必须将每个 ElseIf 条件的否定版本添加到第一个 If 条件。
另一个示例是以下开关:
Select Case i
Case 0 To 40
value = 0
Case 41 To 60
value = 1
Case 61 To 80
value = 3
Case 81 To 100
value = 5
Case Else
value = 0 'Non-compliant
在 switch 中使用最后一个 case 应该没有任何问题。的确,我可以事先将 value
初始化为 0 并忽略最后一种情况,但这样我就会有一个不必要的赋值操作。 Java 规则集让我总是在每个开关中放置一个 default
案例。
S1764:二元运算符的两边不应使用相同的表达式
此规则似乎没有考虑到某些函数在您每次调用它们时可能 return 不同的值,例如访问元素的集合将其从集合中删除:
stack.Push(stack.Pop() / stack.Pop()) 'Non-compliant
不过,我理解这是否过于边缘情况而无法为其设置特殊例外。
以下规则我不太确定:
- S3385:不应使用 "Exit" 语句
虽然我同意 Return
比 Exit Sub
更具可读性,但使用单个 Exit For
来打破 For
或 For Each
循环真的很糟糕吗? Java 的 SonarLint 规则允许在将其标记为问题之前在循环中使用单个 break;
。 VB.NET 中的默认值在这方面更严格是有原因的吗?还是该规则建立在您可以使用 LINQ 扩展方法和 lambda 解决几乎所有循环问题的假设之上?
- S2374:有符号类型应该优先于无符号类型
这条规则基本上说明了根本不应该使用无符号类型,因为它们 "have different arithmetic operators than signed ones - operators that few developers understand"。在我的代码中,我只使用 UInteger 作为 ID 值(因为我不需要负值,而 Long 在我的情况下会浪费内存)。它们存储在 List(Of UInteger) 中,并且只与其他 UIntegers 进行比较。这条规则是否与我的案例相关(规则中提到的 "arithmetic operators" 的比较部分)以及陷阱到底是什么?如果不是,那么将该规则应用于涉及无符号类型的算术运算而不是它们的声明不是更好吗?
S2355:应该使用数组字面量而不是数组创建表达式
也许我对 VB.NET 的了解还不够,但是在以下情况下,我要创建一个初始化长度仅在运行时才知道的固定大小的数组,我将如何准确地满足此规则?这是假阳性吗?
Dim myObjects As Object() = New Object(someOtherList.Count - 3) {} 'Non-compliant
当然,我可能只使用 List(Of Object)。不过我也很好奇
感谢您提出这些观点。请注意,并非所有规则都适用。在某些情况下,我们需要在错误的 positives/false negatives/real 情况之间进行平衡。例如,在运算符规则的两边都有相同的表达式。具有相同的操作数是错误吗?不,这不对。如果是,那么编译器会报告它。难闻的气味,通常是错误的吗?在很多情况下是的。例如,请参阅 Roslyn。我们是否应该调整此规则以排除某些情况?是的,我们应该,2 << 2
没有错。因此,需要进行很多平衡,我们尝试满足为用户带来最大价值的实现。
对于您提出的点数:
- 相同条件结构中的两个分支不应具有完全相同的实现
这条规则通常指出两个代码块完全匹配是一个不好的迹象。出于多种原因,应该避免复制粘贴代码,例如,如果您需要在一个地方修复代码,那么您也需要在另一个地方修复它。你是对的,添加否定条件会是一团糟,但如果你将每个条件提取到它自己的方法中(并调用其中的否定方法)并使用适当的名称,那么它可能会提高代码的可读性。
对于Select Case
,同样,复制粘贴代码总是一个坏兆头。在这种情况下,您可以这样做:
Select Case i
...
Case 0 To 40
Case Else
value = 0 ' Compliant
End Select
或者干脆去掉 0-40 的大小写。
- 二元运算符的两边不应使用相同的表达式
我认为这是一个极端案例。见答案第一段
- "Exit" 不应使用语句
几乎总是正确的是,通过选择另一种循环类型或更改停止条件,您可以在不使用任何 "Exit" 语句的情况下逃脱。最好从循环中设置一个出口点。
- 有符号类型应该优先于无符号类型
这是 SonarQube VB.NET 的遗留规则,我同意你的看法,它不应该在 SonarLint 中默认启用。我在我们的 JIRA 中创建了以下票证:https://jira.sonarsource.com/browse/SLVS-1074
- 应该使用数组字面量而不是数组创建表达式
是的,这似乎是误报,我们不应该在明确指定大小时报告数组创建。 https://jira.sonarsource.com/browse/SLVS-1075
我在 Java 中遇到的绝大多数 SonarLint 规则似乎都是合理且合理的。然而,自从我开始为 VB.NET 使用 SonarLint 以来,我遇到了一些规则,这些规则让我质疑它们的用处,甚至质疑它们是否正常工作。
我想知道这是否只是我以次优方式使用某些 VB.NET 构造的问题,或者规则是否真的存在缺陷。 (抱歉,如果这个问题有点长。我不知道是否应该为每个单独的规则创建一个单独的问题。)
我发现以下规则会留下一些未考虑的情况,这些情况实际上会变成误报:
S1871:同一个条件结构中的两个分支不应该有完全相同的实现
我发现这个为我带来了很多误报,因为有时检查条件的顺序确实很重要。以下列伪代码为例:If conditionA() Then doSomething() ElseIf conditionB() AndAlso conditionC() Then doSomethingElse() ElseIf conditionD() OrElse conditionE() Then doYetAnotherThing() '... feel free to have even more cases in between here Else Then doSomething() 'Non-compliant End If
如果我想遵循此 Sonar 规则并仍然使代码以相同的方式运行,我必须将每个 ElseIf 条件的否定版本添加到第一个 If 条件。
另一个示例是以下开关:Select Case i Case 0 To 40 value = 0 Case 41 To 60 value = 1 Case 61 To 80 value = 3 Case 81 To 100 value = 5 Case Else value = 0 'Non-compliant
在 switch 中使用最后一个 case 应该没有任何问题。的确,我可以事先将
value
初始化为 0 并忽略最后一种情况,但这样我就会有一个不必要的赋值操作。 Java 规则集让我总是在每个开关中放置一个default
案例。S1764:二元运算符的两边不应使用相同的表达式
此规则似乎没有考虑到某些函数在您每次调用它们时可能 return 不同的值,例如访问元素的集合将其从集合中删除:stack.Push(stack.Pop() / stack.Pop()) 'Non-compliant
不过,我理解这是否过于边缘情况而无法为其设置特殊例外。
以下规则我不太确定:
- S3385:不应使用 "Exit" 语句
虽然我同意Return
比Exit Sub
更具可读性,但使用单个Exit For
来打破For
或For Each
循环真的很糟糕吗? Java 的 SonarLint 规则允许在将其标记为问题之前在循环中使用单个break;
。 VB.NET 中的默认值在这方面更严格是有原因的吗?还是该规则建立在您可以使用 LINQ 扩展方法和 lambda 解决几乎所有循环问题的假设之上? - S2374:有符号类型应该优先于无符号类型
这条规则基本上说明了根本不应该使用无符号类型,因为它们 "have different arithmetic operators than signed ones - operators that few developers understand"。在我的代码中,我只使用 UInteger 作为 ID 值(因为我不需要负值,而 Long 在我的情况下会浪费内存)。它们存储在 List(Of UInteger) 中,并且只与其他 UIntegers 进行比较。这条规则是否与我的案例相关(规则中提到的 "arithmetic operators" 的比较部分)以及陷阱到底是什么?如果不是,那么将该规则应用于涉及无符号类型的算术运算而不是它们的声明不是更好吗? S2355:应该使用数组字面量而不是数组创建表达式
也许我对 VB.NET 的了解还不够,但是在以下情况下,我要创建一个初始化长度仅在运行时才知道的固定大小的数组,我将如何准确地满足此规则?这是假阳性吗?Dim myObjects As Object() = New Object(someOtherList.Count - 3) {} 'Non-compliant
当然,我可能只使用 List(Of Object)。不过我也很好奇
感谢您提出这些观点。请注意,并非所有规则都适用。在某些情况下,我们需要在错误的 positives/false negatives/real 情况之间进行平衡。例如,在运算符规则的两边都有相同的表达式。具有相同的操作数是错误吗?不,这不对。如果是,那么编译器会报告它。难闻的气味,通常是错误的吗?在很多情况下是的。例如,请参阅 Roslyn。我们是否应该调整此规则以排除某些情况?是的,我们应该,2 << 2
没有错。因此,需要进行很多平衡,我们尝试满足为用户带来最大价值的实现。
对于您提出的点数:
- 相同条件结构中的两个分支不应具有完全相同的实现
这条规则通常指出两个代码块完全匹配是一个不好的迹象。出于多种原因,应该避免复制粘贴代码,例如,如果您需要在一个地方修复代码,那么您也需要在另一个地方修复它。你是对的,添加否定条件会是一团糟,但如果你将每个条件提取到它自己的方法中(并调用其中的否定方法)并使用适当的名称,那么它可能会提高代码的可读性。
对于Select Case
,同样,复制粘贴代码总是一个坏兆头。在这种情况下,您可以这样做:
Select Case i
...
Case 0 To 40
Case Else
value = 0 ' Compliant
End Select
或者干脆去掉 0-40 的大小写。
- 二元运算符的两边不应使用相同的表达式
我认为这是一个极端案例。见答案第一段
- "Exit" 不应使用语句
几乎总是正确的是,通过选择另一种循环类型或更改停止条件,您可以在不使用任何 "Exit" 语句的情况下逃脱。最好从循环中设置一个出口点。
- 有符号类型应该优先于无符号类型
这是 SonarQube VB.NET 的遗留规则,我同意你的看法,它不应该在 SonarLint 中默认启用。我在我们的 JIRA 中创建了以下票证:https://jira.sonarsource.com/browse/SLVS-1074
- 应该使用数组字面量而不是数组创建表达式
是的,这似乎是误报,我们不应该在明确指定大小时报告数组创建。 https://jira.sonarsource.com/browse/SLVS-1075