Re: [RESOLVED] String.Format
Just a word or two on String.Format itself. String.Format is generally better for readability if you're inserting variables into literal text if there are more than three parts. If you're using VB 2015 or later, you can often improve readability further by using string interpolation, e.g. this:
vb.net Code:
exCaseFailure.strMessage = String.Format("{0} {1} StackTrace: {2}", glbstrMessage, vbNewLine, ex.ToString)
would become this:
vb.net Code:
exCaseFailure.strMessage = $"{glbstrMessage} {vbNewLine} StackTrace: {ex}"
Note that calling ToString with no arguments on a parameter is pointless because String.Format calls ToString on everything internally anyway.
String.Format may still be preferable for long strings or if you want to use the same parameter multiple times, because you can break the expression over multiple lines, e.g.:
vb.net Code:
Dim str = String.Format("First Line: {1}{0}Second Line: {2}{0}Third Line: {3}",
ControlChars.NewLine,
firstLine,
secondLine,
thirdLine)
rather than:
vb.net Code:
Dim str = $"First Line: {firstLine}{ControlChars.NewLine}Second Line: {secondLine}{ControlChars.NewLine}Third Line: {thirdLine}"
It's also worth noting that, like C#, VB 2017 and (I think) VB 2015 also support multiline String literals:
vb.net Code:
Dim str = $"First Line: {firstLine}
Second Line: {secondLine}
Third Line: {thirdLine}"
Re: [RESOLVED] String.Format
Code:
Case Else
Try
Throw CaseError
Catch ex As Exception
Dim exCaseFailure As New ErrorCode1003
exCaseFailure.strMessage = String.Format("{0} {1} StackTrace: {2}", glbstrMessage, vbNewLine, ex.ToString)
MsgBox(exCaseFailure.strMessage)
End Try
End Select
No... Not quite. you have all the right pieces, just not in the right place. Create the New ErrorCode1003 and set it's message in the try and throw it there.... you then Catch the exception and display the message... you shouldn't be creating a new exception type in the catch
Code:
Try
Select case ....
.... other cases
Case Else
Throw New ErrorCode1003 (String.Format("{0} {1} StackTrace: {2}", glbstrMessage, vbNewLine, ex.ToString)) ' assuming you also followed Si's example of changing the New constructor
End Select
Catch ex As ErrorCode1003 'Catch specific errors first
MessageBox.Show(ex.strMessage)
Catch ex As Exception 'Then generic ones later
MessageBox.Show(ex.ToString)
End Try
Actually in looking at that.... the try catch shouldn't be in the case else...that's how I'd structure it...
-tg
Re: [RESOLVED] String.Format
Quick protips, your exception implementations are going in weird directions.
Every Exception should ultimately derive from "Exception". ApplicationException derives from it. That means every exception has two very important properties it should provide a way to set.
- Message is a string that describes "what happened", and hopefully "a guess at how to fix it".
- InnerException is an Exception-type property that is used when you're throwing one exception "because of" another exception.
When you make your custom exceptions, you can add your own custom data. It's customary and a good idea to make these ReadOnly properties set only by the constructor. They represent what happened when something went wrong: if you can change them later you will lose that state.
So your strMessage property is redundant: exceptions already have a String property named Message you should be using instead. This is a guidelines-adhering way to implement your exception:
Code:
Public Class ErrorCode1003Exception
Inherits ApplicationException
Public Sub New(ByVal message As String)
Me.New(message, Nothing)
End Sub
Public Sub New(ByVal message As String, ByVal innerException As Exception)
MyBase.New(message, innerException)
End Sub
End Class
This lets the caller set the base class's Message and InnerException properties if needed. The proper usage would look like:
Code:
Case Else
Throw New ErrorCode1003Exception("Unrecognized value in Case statement.")
End If
The exception already has a stack trace. If you want to format that and display it, you should do that at the site where you catch it.
Code:
Catch ex As ErrorCode1003Exception
Dim formattedError = String.Format("{0} {1}...", glblstrMessage, vbNewLine, ex.StackTrace)
MessageBox.Show(formattedError)
End Try
This is getting sort of difficult to demonstrate without actually writing a test application, but I just got a lot of new work dumped on me.
Re: [RESOLVED] String.Format
Too true and I am already working on that. And I am actually already beginning the work on that. If what I already have appears to be a little wonky, that is because I am still figuring out exactly how these can be used.
Basically, it appears to me that I need to use exception class that is closer to be specific to the exception that I want to throw. That being said, here is how I think things should work.
It appears to me that there are two types of exceptions (I do not know the terminology for this so I will describe them). The first might be one like this:
Code:
Try
'Save change request
Me.Validate()
Me.LnkChangeRequestBindingSource.EndEdit()
Me.LnkChangeRequestTableAdapter.Update(Me._MasterBase4_0ItemMasterDataSet)
'Update item record
UpdateItem()
TblItemMasterTableAdapter.FillBySiTechID(Me._MasterBase4_0ItemMasterDataSet.tblItemMaster, glbintIDNum)
_MasterBase4_0ItemMasterDataSet.tblItemMaster(0).blnEffective = False
_MasterBase4_0ItemMasterDataSet.tblItemMaster(0).blnObsolete = False
_MasterBase4_0ItemMasterDataSet.tblItemMaster(0).blnSupersede = True
_MasterBase4_0ItemMasterDataSet.tblItemMaster(0).blnValidated = False
'Save item
Me.Validate()
Me.tblItemMasterBindingSource.EndEdit()
Me.TblItemMasterTableAdapter.Update(Me._MasterBase4_0ItemMasterDataSet)
glbblnSaved = True
Catch ex As Exception
'Save failure error message
Dim exQueryFailure As New ErrorCode1002
Dim strMessage As String = "Query/Save failed."
MsgBox(exQueryFailure.strMessage = String.Format("Message: {0} {1} {2} {3} StackTrace: {4}", ex.Message, vbNewLine, strMessage, vbNewLine, ex.StackTrace))
End Try
End Sub
I would call this a self throwing exception.
Code:
Try
Throw CaseError
Catch ex As Exception
Dim exCaseFailure As New ErrorCode1003
Dim strMessage As String = "No Case defined in SaveBeforeClosing()"
exCaseFailure.strMessage = String.Format("Message: {0} {1} {2} {3} StackTrace: {4}", ex.Message, vbNewLine, strMessage, vbNewLine, ex.StackTrace)
MsgBox(exCaseFailure.strMessage)
Me.Close()
mnuItemMaster.Show()
End Try
I would call this a none self throwing exception.
So I do have a few questions for you.
In the first case the Try has several failure modes (the first is the query call, the second is any instance of the data transfer from one table to the other table and the third is the Update(). So what I have done is in my namespace I have set an exception class that I believe is more closely related to these failure modes (I am not sure whether a software SME would use the term failure modes, but it is a common term used in engineering for what can go wrong. in this case something that would throw an exception. So I am using the term failure mode to describe something that might throw an exception). Anyway, since there are possible failure modes it would appear to me that I do not need to manually throw an exception, where in the second case a manual throw is required. In both cases I call an exception class that I believe to be more relevant to the condition.
So am I on the right track here? One other thing that I am concerned about is what all these exception and exception classes do to the efficiency of the overall application. i.e., do having all of these exceptions significantly reduce performance? It seems to me that unless an exception is actually run that there would be no change in performance and that until an exception is thrown it is just code sitting there that has no effect on performance. Is that correct?
Re: [RESOLVED] String.Format
I don't think you're too far off! I'm going to explain it in my own words so we can make sure we're on the same page.
The rule in .NET development is a method is supposed to do what it says OR throw an exception if it can't. So a function like File.OpenRead() has that contract. If it succeeds, it returns a valid FileStream. If it fails, it will throw one of 8 different exceptions to try and help you understand why it failed.
Now let's say I'm trying to write a function that opens a file and returns the first line from it. I could write it like this:
Code:
Public Function GetFirstLine(ByVal path As String) As String
Using file = File.OpenRead(path)
Using reader As New StreamReader(file)
Return reader.ReadLine()
End Using
End Using
End Function
I didn't personally throw any exceptions here. But since File.OpenRead() might throw exceptions, those will "bubble up" past my method and someone who calls my method can handle them. But that's a little bit ugly on the part of the caller: they have to *know* you're calling File.OpenRead(). And if you ever switch up your filesystem API, everything that calls GetFirstLine() might have to change the exceptions it handles. That's a bother, especially when you write a lot of code being used by other people.
So there's a million different ways I might approach it, but let's start with what I do when prototyping. I know this method might fail, and I'm not quite sure which failures I care about. I just make a generic GetFirstLineException and throw that. So the 2nd attempt looks like this:
Code:
Public Function GetFirstLine(ByVal path As String) As String
Try
Using file = File.OpenRead(path)
Using reader As New StreamReader(file)
Return reader.ReadLine()
End Using
End Using
Catch ex As Exception
Dim message = "Failed to read the first line of a file."
Throw New GetFirstLineException(message, ex)
End Catch
End Function
This is what you think about inside of every method you write. "Do I want to let exceptions that happen here get out, or do I want to present a more granular view of my errors at this point?" Most people don't worry about it, because they don't write code for other people.
Anyway, at the call site, let's say I'm trying to display a greeting to the user, and the file is supposed to have their name. I might write this:
Code:
Dim name As String = ""
Try
name = GetFirstLine("test.txt")
Catch ex As GetFirstLineException
MessageBox.Show("Oops! Something is wrong with your file and I can't do anything.")
Return
End Try
MessageBox.Show($"Hello, {name}, how are you today?")
But there I'm making a decision: "Whoever calls this code can deal if the name isn't read." What if they can't? Then I'd probably just write this:
Code:
Try
Dim name As String = GetFirstLine()
MessageBox.Show($"Hello, {name}, how are you today?")
Catch ex As GetFirstLineException
Throw New InvalidUserFileException("Couldn't get the name!", ex)
End Try
So when you write any code, you have to ask yourself what it should do. It really has three choices:
- If it can handle the exception and do something about it, then it should do that and not throw anything.
- If it can't do anything to "fix" the problem, it might choose to throw another exception to tell whoever called it something went wrong.
- If it can't do anything to "fix" the problem, but doesn't want to throw an exception, it has to do SOMETHING to satisfy the expectations of the code that called it.
It takes a lot of experience to decide when you're making things worse with new exceptions, and when you're making them better. But the rule of thumb you should always come back to is any Sub/Function you write should either do what it says or throw an exception. Sometimes "throwing" just means "not handling the ones the code I call might throw". Other times it means repackaging them as friendlier exceptions.
Re: [RESOLVED] String.Format
I can see that Sitten. I would interpret what you are saying (in general terms from the world I used to inhabit) as doing a FMEA (Failure Mode Effects Analysis). What that is, is a formalized process for defining all the failure modes (it is impossible to define all of them) and building the remediation (Corrective action) in whatever system is being created during what I would call the Design Phase of what is a formalized Design Control process. Essentially, this means that before you build a system you define what the user and functional requirements are and then define what can go wrong and build the fix into the system as it is being designed.
The biggest cause of any failure mode (including software applications) is always some form of user interaction/inputs. The rest are the other types of failure modes in the code itself, such as in my code above where a query can fail, the Update() can fail or the data transfer between tables can fail. So in the three points you made what I have to admit is that I have not even looked at (yet) the third point, which is that none of my exceptions offer a means to meet the requirements of the code that called it.
Had I already defined a full set of requirements for the application and the code within and had I done any Design Control and at least done an FMEA I could have built all of that into my exceptions already. At any rate, I do understand the process, the functional requirements and the user requirements needed for an application (even though I have not been doing much to carry any of that out).
I believe my background provides me with enough experience to recognize and understand your points, which are well made and everyone should be acquainted with. Although I will admit that I have been doing very little to apply those principles (hey, I am retired). At this point in time I believe that I need to understand two things. The first is how to consistently identify a possible failure mode in the code itself and/or where user interaction can cause a failure mode. The second is the best methodology of corrective action for the remediation of identified failure modes.