Related
The large majority of SonarLint rules that I've come across in Java seemed plausible and justified. However, ever since I've started using SonarLint for VB.NET, I've come across several rules that left me questioning their usefulness or even whether or not they are working correctly.
I'd like to know if this is simply a problem of me using some VB.NET constructs in a suboptimal way or whether the rule really is flawed.
(Apologies if this question is a little longer. I didn't know if I should create a separate question for each individual rule.)
The following rules I found to leave some cases unconsidered that would actually turn up as false-positives:
S1871: Two branches in the same conditional structure should not have exactly the same implementation
I found this one to bring up a lot of false-positives for me, because sometimes the order in which the conditions are checked actually does matter. Take the following pseudo code as example:
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
If I wanted to follow this Sonar rule and still make the code behave the same way, I'd have to add the negated version of each ElseIf-condition to the first If-condition.
Another example would be the following switch:
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
There shouldn't be anything wrong with having that last case in a switch. True, I could have initialized value beforehand to 0 and ignored that last case, but then I'd have one more assignment operation than necessary. And the Java ruleset has conditioned me to always put a default case in every switch.
S1764: Identical expressions should not be used on both sides of a binary operator
This rule does not seem to take into account that some functions may return different values every time you call them, for instance collections where accessing an element removes it from the collection:
stack.Push(stack.Pop() / stack.Pop()) 'Non-compliant
I understand if this is too much of an edge case to make special exceptions for it, though.
The following rules I am not actually sure about:
S3385: "Exit" statements should not be used
While I agree that Return is more readable than Exit Sub, is it really bad to use a single Exit For to break out of a For or a For Each loop? The SonarLint rule for Java permits the use of a single break; in a loop before flagging it as an issue. Is there a reason why the default in VB.NET is more strict in that regard? Or is the rule built on the assumption that you can solve nearly all your loop problems with LINQ extension methods and lambdas?
S2374: Signed types should be preferred to unsigned ones
This rule basically states that unsigned types should not be used at all because they "have different arithmetic operators than signed ones - operators that few developers understand". In my code I am only using UInteger for ID values (because I don't need negative values and a Long would be a waste of memory in my case). They are stored in List(Of UInteger) and only ever compared to other UIntegers. Is this rule even relevant to my case (are comparisons part of these "arithmetic operators" mentioned by the rule) and what exactly would be the pitfall? And if not, wouldn't it be better to make that rule apply to arithmetic operations involving unsigned types, rather than their declaration?
S2355: Array literals should be used instead of array creation expressions
Maybe I don't know VB.NET well enough, but how exactly would I satisfy this rule in the following case where I want to create a fixed-size array where the initialization length is only known at runtime? Is this a false-positive?
Dim myObjects As Object() = New Object(someOtherList.Count - 3) {} 'Non-compliant
Sure, I could probably just use a List(Of Object). But I am curious anyway.
Thanks for raising these points. Note that not all rules apply every time. There are cases when we need to balance between false positives/false negatives/real cases. For example with identical expressions on both sides of an operator rule. Is it a bug to have the same operands? No it's not. If it was, then the compiler would report it. Is it a bad smell, is it usually a mistake? Yes in many cases. See this for example in Roslyn. Should we tune this rule to exclude some cases? Yes we should, there's nothing wrong with 2 << 2. So there's a lot of balancing that needs to happen, and we try to settle for an implementation that brings the most value for the users.
For the points you raised:
Two branches in the same conditional structure should not have exactly the same implementation
This rule generally states that having two blocks of code match exactly is a bad sign. Copy-pasted code should be avoided for many reasons, for example if you need to fix the code in one place, you'll need to fix it in the other too. You're right that adding negated conditions would be a mess, but if you extract each condition into its own method (and call the negated methods inside them) with proper names, then it would probably improves the readability of your code.
For the Select Case, again, copy pasted code is always a bad sign. In this case you could do this:
Select Case i
...
Case 0 To 40
Case Else
value = 0 ' Compliant
End Select
Or simply remove the 0-40 case.
Identical expressions should not be used on both sides of a binary operator
I think this is a corner case. See the first paragraph of the answer.
"Exit" statements should not be used
It's almost always true that by choosing another type of loop, or changing the stop condition, you can get away without using any "Exit" statements. It's good practice to have a single exit point from loops.
Signed types should be preferred to unsigned ones
This is a legacy rule from SonarQube VB.NET, and I agree with you that it shouldn't be enabled by default in SonarLint. I created the following ticket in our JIRA: https://jira.sonarsource.com/browse/SLVS-1074
Array literals should be used instead of array creation expressions
Yes, it seems to be a false positive, we shouldn't report on array creations when the size is explicitly specified. https://jira.sonarsource.com/browse/SLVS-1075
I'm working on refactoring a decently large project, and I'm interested in searching out and reducing Code Clone for better standardization as well as ease of development.
I've got a code snippet that keeps coming up in "Exact Matches" (using Visual Studio 2012's
"Find Code Clones" feature).
Here it is:
End If
End Using
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Using
End Using
Return Nothing
Basically, I have a whole host of similar functions (similar in structure but not in actual function) that all open with something like this:
Const sql As String = ...
Using cn As SqlConnection...
Using cmd As SqlCommand(sql,cn)
... Maybe add some SqlParameters
cn.Open()
... Something with cmd.Execute...
Now, I recognize that the first code block is IDENTICAL across many, many methods, but I can't figure out a way to pull that code out, write it once and simply call it each time I need that functionality. It seems to me that there is too much control flow occurring.
So, I'm stumped as to how to fix this (and I'm only assuming that I can "fix" this because Microsoft has identified it as "cloned code").
I'm thinking of something along the lines of making a small number of functions that do the same type of thing (like returning a count from a table, returning a top value, etc...) and only really differ by the SQL that is executed. That could be a bit tricky, though, since sometimes the parameters (type and number) differ.
Any thoughts?
I wouldn't concern yourself with those. Your common-sense first impression is correct. While, technically speaking, the syntax is repeated multiple times, it is not really a repetition of logic or an algorithm of any kind. That's not to say there's no way to reduce that repetition, it's just that by doing so, you will likely end up with a worse design in your code.
For instance, you could create a single method that does all the setup and tear-down and then just calls a method in the middle that actually uses the connection to do the work, such as:
Public Function PerformDbTask(task As IMyDbTask)
Using cn As SqlConnection...
Using cmd As SqlCommand = cn.CreateCommand()
Try
cn.Open()
task.Perform(cmd)
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Using
End Using
Return Nothing
End Function
However, what have you really gained? Probably not much at all, yet you've lost a lot of flexibility. So, unless that kind of design is actually necessary for what you are trying to do, I wouldn't waste time trying to solve a problem that doesn't exist.
You could create a class that implements a builder pattern, that winds up looking like
Dim list =
SqlBuilder.Query("SELECT ...")
.WithConnection("...connection string...")
.WithParameter(userName)
.WithParameter(lastTrackId)
.RetrieveTopRows(10)
Your problem is you are using a clone detector that matches token sequences (thus it matches the end-of-block sequences you exhibited), as opposed to clone detectors that match code structures. While the token sequence is technically a clone, it isn't an interesting clone. Token clone detectors produce many many "false positive" clones like this, which simply waste your time.
It is easier to build token sequence detectors, which is why they built that into MS Studio.
If you want better detectors, you have to step outside of Studio.
You should look into clone detectors that match on abstract syntax trees. They don't produce this kind of false positive, and they can find clones with complex parameters (as opposed to single-token parameters).
You should also understand that just because something has been identified as a clone, that it is not always easy or possible to refactor it away. The language you have may not have sufficiently strong abstraction mechanisms to handle that case.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 5 years ago.
Improve this question
Is it considered bad practice to use colons to put two statements on the same line in Visual Basic?
There is nothing inherently wrong with using the colon to combine statements. It really depends on the context but as long as it doesn't reduce readability, there is nothing wrong with it.
As a general rule I avoid using the colon for this purpose. I find it's more readable to have one statement per line. However this is not a colon specific issue. I avoid doing the same with a semi-colon in C# or C++. It's just a personal preference.
It's a good practice in moderation, because sometimes readability is enhanced by concatenating two lines:
when the lines are short and intimately
related
when the lines are short and trivial
Option Compare Database: Option Explicit ''My favorite!
rsDataSet.Close: Set rsDataSet= Nothing
Don't do it if:
it hurts readability.
it complicates debugging. Control structures such as If...Then need to stay clean. You'll be glad you kept it simple when it's time to set a break point.
it compromises future editing. Often you want to keep sections portable. Moving or re-structuring a block of code is easily hindered by attempts to minimize your code.
In general, I'd advise against it, as it makes for busier code.
However, for simple tasks, there is nothing wrong with it. For instance:
for i = 1 to 10: ProcessFoo(i): next
I think a line like this is short enough not to cause confusion.
I'll take the other side. I don't like dense lines of code. It is easier to skim code when lines are not combined.
Combining statements also makes it easier to create long functions that still fit on a single screen.
It isn't a major sin, I just don't like it.
I also don't like one line If statements.
To me you shouldn't say "never do thus", you should just say "If you do this, a possible problem is such and such." Then just weigh the pros and cons for yourself. The pro is brevity/few lines of code. Sometimes this can aid readability. For instance some people use it to do vb.Net declarations:
Dim x As Long: x = 1
Or wait loops:
Do Until IE.ReadyState = READYSTATE_COMPLETE: DoEvents: Loop
But obviously you can really make it rough on someone too:
Public Sub DoYouKnowWhatThisDoes()
MsgBox Example
End Sub
Private Function Example()
Const s$ = "078243185105164060193114247147243200250160004134202029132090174000215255134164128142"
Const w% = 3: Const l% = 42: Dim i%, r$: For i = 1 To l Step w: r = r & ChrW$(Mid$(s, i, w) Xor Mid$(s, i + l, w)): Next: Example = r
End Function
Another practical reason that you might not want to use this approach is breakpoints. Breakpoints can only be set by the line. So if you have several things executing on the same line you can't isolate the second thing. It will stop on the first statement. (This is also one of the reasons some people don't like single line ifs.) It just complicates debugging a little.
I usually don't use colons in production code for this reason. However I do use them to improve the brevity of "copy/paste" code that I post in forums and elsewhere. YMMV:)
I realize this is a very old question, but it was the first result on my Google search, so I hope I can be forgiven for chiming in here.
There is one situation (exactly what led me here in fact) in which this approach is not only useful, it's the only way to accomplish the desired result: the Immediate window. Any code you want to execute in the Immediate window must all be on one line. So in order to use any form of Do, Case, For, While, or With in the Immediate window, you will need to use colons.
It is considered bad practice in most of the sites at which I have worked. And by most of the VB developers with whom I have worked. And in my head. If I see it, I will admit that I would almost certainly change it. I say "almost" because I admit there's a possibility that I could find a piece of code that looked better that way. I don't expect to see it in my lifetime, though.
I also really don't like one-line **If**s either.
Both are most likely hangovers from the days of VGA (640x480) monitors; that's no excuse these days.
I've only ever used it when I'm clsoing a recordset and setting the variable to nothing. I figure one line instead of two gives me more lines of code on the screen and doesn't detract from readability.
I've seen it used in simple select cases such as the following but that would be as far as I would go.
Select Case success
Case ERROR_FILE_NO_ASSOCIATION: msg = "no association"
Case ERROR_FILE_NOT_FOUND: msg = "file not found"
Case ERROR_PATH_NOT_FOUND: msg = "path not found"
Case ERROR_BAD_FORMAT: msg = "bad format"
from http://vbnet.mvps.org/index.html?code/system/findexecutable.htm
And even then I would've lined up the "msg =" portion.
I've never seen this mentioned in an official capacity at any of the companies which I've worked. But I do think that using colons excessively can start to make your code less readable and more of a pain to maintain.
I do tend to use these myself at times, for example when checking for cancel in one of my recent projects:
If _bCancel Then Status = CancelProcess() : Return Status
Putting this in kept my code readable than the alternative IF block.
But it can be taken too far, I've recently inherited a project which is replete with examples of taking colon usage too far :
Select Case GetStringValue(Index).Trim.ToLower
Case "yes", "y" : GetBooleanValue = True
Case "no", "n" : GetBooleanValue = False
Case Else : GetBooleanValue = Nothing
End Select
Personally I find the above to be a bit much.
I like this one
Using pro As New Process() : With pro
...
End With
End Using
I've seen it used in class declarations when using inheritance or implementing an interface:
Public Class DerivedClass : Inherits BaseClass
...
End Class
But like the others, I also discourage its use.
Chris
The answer to the question is No. Anything beyond No is purely subjective and wasteful irrespective of the answer outside of a simple No. Below is my waste of typing.
Are you some sort of slave? Do as you wish. You are the center of your universe not some stranger from StackOverflow. If you work for a company the question is mute because coding style would already be defined and completely out of your control. As for one's self, who in this universe is going to ever in all eternity look at let along care about your code.
I would choose A over B. As evident this shows the purpose of a colon without the use of a colon. It's to save space. Below saves space and makes code far more readable. Keeps It Simple Stupid. Same for ternary ? : usage. When the code is inherently complex then a colon, single line if then else, or ternary no longer should be considered.
'================================================================
'A
If somevalue1 = 0 Then AddLogTry("True") Else AddLogFalse("False")
If somevalue2 = 0 Then AddLogTry("True") Else AddLogFalse("False")
If somevalue3 = 0 Then AddLogTry("True") Else AddLogFalse("False")
'================================================================
'================================================================
'B
If somevlaue1 = 0 Then
AddLogTrue("True")
Else
AddLogFalse("False")
EndIf
If somevlaue2 = 0 Then
AddLogTrue("True")
Else
AddLogFalse("False")
EndIf
If somevlaue3 = 0 Then
AddLogTrue("True")
Else
AddLogFalse("False")
EndIf
'================================================================
I have process that needs to create a bunch of records in the database and roll everything back if anything goes wrong. What I want to do is this:
Public Structure Result
Public Success as Boolean
Public Message as String
End Structure
Private _Repository as IEntityRepository
Public Function SaveOrganization( _
ByVal organization As rv_o_Organization) As Result
Dim result = Result.Empty
_Repository.Connection.Open()
_Repository.Transaction = _Repository.Connection.BeginTransaction()
''//Performs validation then saves it to the database
''// using the current transaction
result = SaveMasterOrganization(organization.MasterOrganization)
If (Not result.Success) Then
GoTo somethingBadHappenedButNotAnException
End If
''//Performs validation then saves it to the database
''//using the current transaction
result = SaveOrganziation(dbOrg, organization)
If (Not result.Success) Then GoTo somethingBadHappenedButNotAnException
somethingBadHappenedButNotAnException:
_Repository.Transaction.Commit()
_Repository.Connection.Close()
Return result
End Sub
Is this an ok use of the GoTo statement, or just really bad design? Is there a more elegant solution? Hopefully this sample is able to get the point across
If you have to ask, don't do it.
For your specific code, you could do it like this:
Public Function SaveOrganization(ByVal organization As rv_o_Organization) As Result
Dim result As Result = Result.Empty
_Repository.Connection.Open()
_Repository.Transaction = _Repository.Connection.BeginTransaction()
'Performs validation then saves it to the database
'using the current transaction
result = SaveMasterOrganization(organization.MasterOrganization)
'Performs validation then saves it to the database
'using the current transaction
If result.Success Then result = SaveOrganziation(dbOrg, organization)
_Repository.Transaction.Commit()
_Repository.Connection.Close()
Return result
End Sub
Goto has such a terrible reputation that it will cause other developers to instantly think poorly of your code. Even if you can demonstrate that using a goto was the best design choice - you'll have to explain it again and again to anyone who sees your code.
For the sake of your own reputation, just don't do it.
Really bad design. Yes.
There might be some extreme edge cases where it is applicable, but almost unequivocally, no, do not use it.
In this specific case, you should be using the Using statement to handle this in a better manner. Generally, you would create a class which would implement IDisposable (or use one that already does) and then handle cleanup in the Dispose method. In this case, you would close the connection to the database (apparently, it is reopened again from your design).
Also, I would suggest using the TransactionScope class here as well, you can use that to scope your transaction and then commit it, as well as auto-abort in the face of exceptions.
The only time you should use a goto, is when there is no other alternative.
The only way to find out if there are no other alternatives is to try them all.
In your particular example, you should use try...finally instead, like this (sorry, I only know C#)
void DoStuff()
{
Connection connection = new Connection();
try
{
connection.Open()
if( SomethingBadHappened )
return;
}
finally
{
connection.Close();
}
}
I would say extremely sparingly. Anytime I have had to think about using a GOTO statement, I try and refactor the code. The only exception I can think of was in vb with the statement On Error Goto.
There is nothing inherently wrong with goto, but this isn't a very ideal use of it. I think you are putting too fine a point on the definition of an exception.
Just throw a custom exception and put your rollback code in there. I would assume you would also want to rollback anyway if a REAL exception occured, so you get double duty out of it that way too.
Gotos are simply an implementation detail. A try/catch is much like a goto (an inter-stack goto at that!) A while loop (or any construct) can be written with gotos if you want. Break and early return statements are the most thinly disguised gotos of them all--they are blatant(and some people dislike them because of the similarity)
So technically there is nothing really WRONG with them, but they do make for more difficult code. When you use the looping structures, you are bound to the area of your braces. There is no wondering where you are actually going to, searching or criss-crossing.
On top of that, they have a REALLY BAD rep. If you do decide to use one, even in the best of possible cases, you'll have to defend your decision against everyone who ever reads your code--and many of those people you'll be defending against won't have the capability to make the judgment call themselves, so you're encouraging bad code all around.
One solution for your case might be to use the fact that an early return is the same as a goto (ps. Worst psuedocode ever):
dbMethod() {
start transaction
if(doWriteWorks())
end Transaction success
else
rollback transaction
}
doWriteWorks() {
validate crap
try Write crap
if Fail
return false
validate other crap
try Write other crap
if Fail
return false
return true
}
I think this pattern would work in VB, but I haven't used it since VB 3 (around the time MS bought it) so if transactions are somehow bound to the executing method context or something, then I dunno. I know MS tends to bind the database very closely to the structure of the code or else I wouldn't even consider the possibility of this not working...
I use goto's all the time in particular places, for example right above a Try Catch, in case a you prompt the user, "Retry?, Cancel", if retry then Goto StartMyTask: and increment the Current Try of Maximum retries depending on the scenario.
They're also handy in for each loops.
For each Items in MyList
If VaidationCheck1(Item) = false then goto SkipLine
If ValidationCheck(Item) = false then goto skipline
'Do some logic here, that can be avoided by skipping it to get better performance.
'I use then like short circuit operands, why evaluate more than you actually have to?
SkipLine:
Next
I wouldn't substitute them for functions and make really huge blocks of long code, just in little places where they can really help, mostly to skip over things.
Everytime I've seen a goto used, simple refactoring could have handle it. I'd reccomend never using it unless you "know" that you have to use it
I'm tempted to say never, but I suppose there is always one case where it may be the best solution. However, I've programmed for the last 20 years or so without using a Goto statement and can't foresee needing one any time soon.
Why not wrap each function call in a try catch block and when that is done, if one of your exceptions are thrown, you can catch it and close the connection. This way, you avoid the GOTO statement altogether.
In short, the GOTO statement is NOT a good thing, except in unusual situations, and even then it is usually a matter of refactoring to avoid it. Don't forget, it is a leftover from early languages, in this case BASIC.
I'd say use very sparingly as it's generally associated with introducing spagetti code. Try using methods instead of Labels.
A good case I think for using GOTO is to create a flow through select which is available in C# but not VB.
The go to statement has a tendency to make the program flow difficult to understand. I cannot remember using it during the last ten years, except in visual basic 6 in combination with "on error".
Your use of the go to is acceptable as far as I am concerned, because the program flow is very clear. I don't think using try ... catch would improve things much, because you would need to throw the exceptions on the locations where the go tos are now.
The formatting, however, is not very appealing:-)
I would change the name of the go to label to something else, because this location is also reached when everything is successful. clean_up: would be nice.
Eh, there was a decent use for it in VBscript/ASP for handling errors. We used it to return error handling back to ASP once we were done using on error resume next.
in .net? Heavens, no!
Everyone says avoid it but why.
The GOTO syntax is a jump statement in assembly - very efficient.
Main reason to avoid it is code readability. You need to find the GOTO label in the code which is hard to eyeball.
Some people think that it might cause memory leaks but I've seen that experts say that this is not true in .NET.
In this question, a user commented to never use the With block in VB. Why?
"Never" is a strong word.
I think it fine as long as you don't abuse it (like nesting)
IMHO - this is better:
With MyCommand.Parameters
.Count = 1
.Item(0).ParameterName = "#baz"
.Item(0).Value = fuz
End With
Than:
MyCommand.Parameters.Count = 1
MyCommand.Parameters.Item(0).ParameterName = "#baz"
MyCommand.Parameters.Item(0).Value = fuz
There is nothing wrong about the With keyword. It's true that it may reduce readibility when nested but the solution is simply don't use nested With.
There may be namespace problems in Delphi, which doesn't enforce a leading dot but that issue simply doesn't exist in VB.NET so the people that are posting rants about Delphi are losing their time in this question.
I think the real reason many people don't like the With keyword is that is not included in C* languages and many programmers automatically think that every feature not included in his/her favourite language is bad.
It's just not helpful compared to other options.
If you really miss it you can create a one or two character alias for your object instead. The alias only takes one line to setup, rather than two for the With block (With + End With lines).
The alias also gives you a quick mouse-over reference for the type of the variable. It provides a hook for the IDE to help you jump back to the top of the block if you want (though if the block is that large you have other problems). It can be passed as an argument to functions. And you can use it to reference an index property.
So we have an alternative that gives more function with less code.
Also see this question:
Why is the with() construct not included in C#, when it is really cool in VB.NET?
The with keyword is only sideswiped in a passing reference here in an hilarious article by the wonderful Verity Stob, but it's worth it for the vitriol: See the paragraph that starts
While we are on identifier confusion. The with keyword...
Worth reading the entire article!
The With keyword also provides another benefit - the object(s) in the With statement only need to be "qualified" once, which can improve performance. Check out the information on MSDN here:
http://msdn.microsoft.com/en-us/library/wc500chb(VS.80).aspx
So by all means, use it.