[RESOLVED] String.Format-VBForums
Results 1 to 23 of 23

Thread: [RESOLVED] String.Format

  1. #1

    Thread Starter
    Hyperactive Member
    Join Date
    Dec 2011
    Location
    Oregon City, Oregon
    Posts
    342

    Resolved [RESOLVED] String.Format

    I have been looking at using String.Format the last couple of days and finding it very interesting. It especially looks useful to me for printing and reports. I do not yet know if that is one of its normal usages or not, but it looks to me like it might work very well for that.

    My question, however, is about something that I got from Mike Strong and would like to know more about.

    The code goes something like this:

    Code:
                    Catch ex As Exception
                        MessageBox.Show(String.Format("Message: {0} {1} StackTrace: {2}", ex.Message, vbNewLine, ex.StackTrace))
                    End Try
    What I am interested in is the StackTrace and under what other conditions this could be called other than As Exception?

  2. #2
    PowerPoster techgnome's Avatar
    Join Date
    May 2002
    Posts
    31,277

    Re: String.Format

    Since it is a property of the Exception class, I don't think it's available outside the exception block there... I could be wrong though.
    Even the documentation https://msdn.microsoft.com/en-us/lib...v=vs.110).aspx only uses it within the catch of an exception.
    That said, I don't see anything that says "Only call this from within an exception." ... hmm... might be worth exploring.

    -tg
    * I don't respond to private (PM) requests for help. It's not conducive to the general learning of others.*
    * I also don't respond to friend requests. Save a few bits and don't bother. I'll just end up rejecting anyways.*
    * How to get EFFECTIVE help: The Hitchhiker's Guide to Getting Help at VBF - Removing eels from your hovercraft *
    * How to Use Parameters * Create Disconnected ADO Recordset Clones * Set your VB6 ActiveX Compatibility * Get rid of those pesky VB Line Numbers * I swear I saved my data, where'd it run off to??? *

  3. #3
    Still learning kebo's Avatar
    Join Date
    Apr 2004
    Location
    Gardnerville,nv
    Posts
    3,616

    Re: String.Format

    You can get the stack trace from any where using the StackTrace Class
    vb.net Code:
    1. Sub Main()
    2.         method1()
    3.     End Sub
    4.  
    5.     Private Sub method1()
    6.         method2()
    7.     End Sub
    8.  
    9.  
    10.     Private Sub method2()
    11.         Dim stack As New StackTrace(True)
    12.         Console.WriteLine(stack.ToString)
    13.     End Sub
    Process control doesn't give you good quality, it gives you consistent quality.
    Good quality comes from consistently doing the right things.

    Vague general questions have vague general answers.
    A $100 donation is required for me to help you if you PM me asking for help. Instructions for donating to one of our local charities will be provided.

    ______________________________
    Last edited by kebo : Now. Reason: superfluous typo's

  4. #4

    Thread Starter
    Hyperactive Member
    Join Date
    Dec 2011
    Location
    Oregon City, Oregon
    Posts
    342

    Re: String.Format

    Thanks Kebo. I figured there was a way to use this more extensively. Anyway I was hoping. I have been looking for a way to do that for awhile but have never used, or even seen, StackTrace before. My motto has now become: "The more I learn the less I know".

  5. #5
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,166

    Re: String.Format

    You can get a stack trace anywhere. If you look at the documentation, you'll find that Exception.StackTrace has a special type. If you follow the documentation you'll find out you can create that type whenever you want.

    But it comes at a cost. Stack traces are very expensive to calculate. They're a big part of why throwing exceptions can be a performance-killer and shouldn't be done in tight loops. When I last used a profiler, I found that getting a stack trace accounted for 500ms every time I did it.

    I'll pay that cost in debugging, but I'll never ship with that kind of code laying around.
    Nothing I post is production-ready. It is provided as-is, use it at your own risk.

  6. #6

    Thread Starter
    Hyperactive Member
    Join Date
    Dec 2011
    Location
    Oregon City, Oregon
    Posts
    342

    Re: String.Format

    Sitten, I can see that and I can see getting rid of it prior to shipping. However, I do not see the cost problem unless it is called. Am I correct in that?

    What I am using these for, in this Case I am using Select Case (I love making puns). This application is still under development and I want to be able to catch when I place something new in the code that I have not yet setup a new Case for. I already have a method for that, but I also wanted to get the line number where it occurs.

    So here is where I am with this right now:

    Code:
        Private Sub CaseExceptionCode1003()
            Dim strMessage As String = Nothing
            Dim TraceLine As New StackTrace(True)
            glbstrMessage = CType(ErrorCodes.Code1003(strMessage), String) +
                vbNewLine +
                _StrError
            Dim ErrorMessage As String = String.Format("{0} StackTrace: {1}", glbstrMessage, vbNewLine, TraceLine.ToString)
            Throw New NotImplementedException(CType(MsgBox(ErrorMessage), String))
        End Sub
    Name:  error.jpg
Views: 77
Size:  36.9 KB

    So I am getting everything except the line number. What could I possibly be doing that is still wrong? (like I expected it to be easy).

  7. #7
    eXtreme Programmer .paul.'s Avatar
    Join Date
    May 2007
    Location
    Chelmsford UK
    Posts
    21,509

    Re: String.Format

    @gwboolean

    Shouldn't that be...

    Code:
    Dim ErrorMessage As String = String.Format("{0} {1}StackTrace: {2}", glbstrMessage, vbNewLine, TraceLine.ToString)
    ???

  8. #8

    Thread Starter
    Hyperactive Member
    Join Date
    Dec 2011
    Location
    Oregon City, Oregon
    Posts
    342

    Re: String.Format

    .Paul.

    I made the change you suggested, but got a lot more than I wanted and the line number was wrong. Line 81 is a line for a property setting in a routine.

    To be honest, throwing an exception is not what I wanted to do anyway.

  9. #9

    Thread Starter
    Hyperactive Member
    Join Date
    Dec 2011
    Location
    Oregon City, Oregon
    Posts
    342

    Re: String.Format

    I left this out of the previous post

    Name:  error.jpg
Views: 76
Size:  43.6 KB.

  10. #10
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,166

    Re: String.Format

    When you call the StackTrace constructor, you get the information for the current frame. So I can tell from this stack trace the story goes something like:

    You clicked a ToolStripMenuItem. That item belongs to mnuItemMaster and the handler is mnuItemPart_Click() on line 93 in mnuItemMaster.vb. It tried to show a form, specifically frmItemList. But the Load event handler for that form did something that called CaseExceptionCode1003(). The offending code is on line 232 of frmItemList.vb.

    What part of that story is wrong? You don't get a lot of control over what stack trace you get: the moment you call the constructor is the stack trace you will generate. If you want it closer to what "caused" the chain of events, you have to move your things that create the stack trace closer.

    Another problem: that NotImplementedException is going to create its own StackTrace at the moment it's thrown. So you're paying a double penalty, and your error messages always have an extra frame or two in them.

    Personally, I pay the "extra frames" price because I know to skip over those frames, and I know how to read a call stack. When I want to thin it out, or if I don't want my error handling message to be part of the frame, this is the kind of pattern I have to follow.

    You have:
    Code:
    Class A
        
        Sub Root()
            DoSomethingDangerous()
        End Sub
    
        Sub DoSomethingDangerous()
            Try
                Dim helper As New B()
                b.Explode()
            Catch ex As Exception
                ???
            End Catch
        End Sub
    
    End Class
    
    Class B
    
        Public Sub Explode()
            HandleError()
        End Sub
    
        Private Sub HandleError()
            Dim trace As New StackTrace(True)
            MessageBox.Show(trace.ToString())
            Throw New NotSupportedException("Boom.")
        End Sub
    
    End Class
    Your stack trace will be displayed showing Root() -> DoSomethingDangerous() -> Explode() -> HandleError(). It can't display anything else because that's how to get there. Here's how to get a more abbreviated stack trace:
    Code:
    Class A
        
        Sub Root()
            DoSomethingDangerous()
        End Sub
    
        Sub DoSomethingDangerous()
            Try
                Dim helper As New B()
                b.Explode()
            Catch ex As Exception
                Dim trace As New StackTrace(True)
                MessageBox.Show(trace.ToString())
                ???
            End Catch
        End Sub
    
    End Class
    
    Class B
    
        Public Sub Explode()
            Throw New NotSupportedException("Boom.")
        End Sub
    
    End Class
    The exception is thrown in Explode(), and its StackTrace property will reflect that. That exception is caught in DoSomethingDangerous(), and the StackTrace will indicate the Catch block of that method. Sadly, there's not a way to make it point to the real troublesome call, which is b.Explode(). If you want to do that, you have to add the information yourself:
    Code:
    Class A
        
        Sub Root()
            Try 
                DoSomethingDangerous()
            Catch ex As Exception
                MessageBox.Show("I tried to do something dangerous and failed."
                ???
            End Try
        End Sub
    
        Sub DoSomethingDangerous()
            Try
                Dim helper As New B()
                b.Explode()
            Catch ex As Exception
                Throw ex
            End Catch
        End Sub
    
    End Class
    
    Class B
    
        Public Sub Explode()
            Throw New NotSupportedException("Boom.")
        End Sub
    
    End Class
    When you call Throw, the internals of the Exception are updated to reflect the StackTrace at that point. This is called "rethrowing". It costs you a little bit more performance, but can be useful if you're certain the downstream error isn't really the important one. Probably the best way to get a "friendly" description of what was going on is to manually write a message like I did in this example.

    These are the general solutions: move the exception handlers, rethrow, or use logging to help pinpoint state.
    Nothing I post is production-ready. It is provided as-is, use it at your own risk.

  11. #11

    Thread Starter
    Hyperactive Member
    Join Date
    Dec 2011
    Location
    Oregon City, Oregon
    Posts
    342

    Re: String.Format

    Sitten, Got it. OK, a little honesty here. I read the content at the bottom of the StackTrace and disregarded the content at the top. So no, it is doing nothing wrong.

    I was thinking about an approach similar to what you are suggesting as an alternative. However, I think I prefer all of the extra content. Also, I see that there are different exception types one can use. Although the content of the StackTrace does not change.

    Anyway, thanks for the clarifications

  12. #12
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,166

    Re: String.Format

    Quote Originally Posted by gwboolean View Post
    Sitten, Got it. OK, a little honesty here. I read the content at the bottom of the StackTrace and disregarded the content at the top. So no, it is doing nothing wrong.

    I was thinking about an approach similar to what you are suggesting as an alternative. However, I think I prefer all of the extra content. Also, I see that there are different exception types one can use. Although the content of the StackTrace does not change.

    Anyway, thanks for the clarifications
    Yeah, on the topic of Exceptions:

    The expectation of the framework designers are that you will make your own exception types. I do it in my application all the time. That way I can catch "TheCoffeeIsTooHotException" instead of "InvalidOperationException". It makes my code much easier to read.

    One trick: ApplicationException was created specifically for this. Nothing in the Framework (that I know of) derives from or throws it. So if you derive all of your custom applications from that, then any Try..Catch that catches ApplicationException is guaranteed to catch only your exceptions, which can be useful. In 10+ years of .NET development I don't think I've met more than 2 people that knew this.

    But Microsoft themselves didn't follow this advice very much and we end up with really clunky exception handling in a lot of mainline APIs. For example, there's plenty of System.IO types with 5+ reasons why they might throw IOException. Sometimes SOME reasons are innocent and OTHERS aren't, but since they all throw the same one it's hard to decide exactly what happened.
    Nothing I post is production-ready. It is provided as-is, use it at your own risk.

  13. #13

    Thread Starter
    Hyperactive Member
    Join Date
    Dec 2011
    Location
    Oregon City, Oregon
    Posts
    342

    Re: String.Format

    That is great about the capability of creating your own exceptions. That is something I would really like to begin doing. However, I haven't even begun to figure out how that is done or what it requires.

    I will look into ApplicationException and see what I can learn and how I can put it to use. Thanks for the heads up on that man.

  14. #14
    PowerPoster techgnome's Avatar
    Join Date
    May 2002
    Posts
    31,277

    Re: String.Format

    It's actually so easy, it's amazing more people don't do it.
    1) Create a class that inherits ApplicationException
    Code:
    Public Class MyCoffeeIsTooHot
      Inherits ApplicationException
    
    End Class
    2) Decide if you need/want to add additional information
    Code:
    Public Class MyCoffeeIsTooHot
      Inherits ApplicationException
    
      Public Property Temperature as Decimal
      Public Property ThresholdTemp as Decimal
    
    End Class
    3) Throw your exception
    Code:
    Dim exCoffeeTooHot as new MyCoffeeIsTooHot
    exCoffeeTooHot.Temperature = 250
    exCoffeeTooHot.ThresholdTemp=120
    Throw exCoffeeTooHot
    4) Catch the exception
    Code:
    try
    'somewhere in here the myCoffeeIsTooHot exception is thrown
    Catch ex as MyCoffeeIstooHot
      ' ex will be an object of type MyCoffeeIsTooHot, 
    ....
    -tg
    * I don't respond to private (PM) requests for help. It's not conducive to the general learning of others.*
    * I also don't respond to friend requests. Save a few bits and don't bother. I'll just end up rejecting anyways.*
    * How to get EFFECTIVE help: The Hitchhiker's Guide to Getting Help at VBF - Removing eels from your hovercraft *
    * How to Use Parameters * Create Disconnected ADO Recordset Clones * Set your VB6 ActiveX Compatibility * Get rid of those pesky VB Line Numbers * I swear I saved my data, where'd it run off to??? *

  15. #15

    Thread Starter
    Hyperactive Member
    Join Date
    Dec 2011
    Location
    Oregon City, Oregon
    Posts
    342

    Re: String.Format

    That is sweet tech. I think I love you man. Although I have to say that from my perspective it is not so simple as it is for you. But I can indeed understand it and I believe that I can implement it.

  16. #16

    Thread Starter
    Hyperactive Member
    Join Date
    Dec 2011
    Location
    Oregon City, Oregon
    Posts
    342

    Re: String.Format

    You da man Tech. I worked through it all and have put this together for a condition. My condition is where I am using Select Case and where none of the Cases are met (Case Else). This only happens when I am putting together an app so that is the only time I actually need this. So here is what I got.

    In a namespace I placed the following code (I like using namespaces because it is so easy to keep track of my crap):

    Code:
        Public Class ErrorCode1003
            Inherits ApplicationException
            Public Property strMessage As String
        End Class
    Then I set my namespace object in the form that I will be running this

    Code:
    Private CaseError As New nspItemMaster.ErrorCode1003
    Now we run exception for Case Else

    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
    Here is the result

    Name:  error.jpg
Views: 70
Size:  35.4 KB

    It still needs a little cleaning up (there is still more message than I want, but I think I know what I need to get rid of for that), but it is damn nice.

  17. #17
    .NUT jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    97,097

    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:
    1. exCaseFailure.strMessage = String.Format("{0} {1} StackTrace: {2}", glbstrMessage, vbNewLine, ex.ToString)
    would become this:
    vb.net Code:
    1. 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:
    1. Dim str = String.Format("First Line: {1}{0}Second Line: {2}{0}Third Line: {3}",
    2.                         ControlChars.NewLine,
    3.                         firstLine,
    4.                         secondLine,
    5.                         thirdLine)
    rather than:
    vb.net Code:
    1. 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:
    1. Dim str = $"First Line: {firstLine}
    2. Second Line: {secondLine}
    3. Third Line: {thirdLine}"
    Why is my data not saved to my database? | MSDN Data Walkthroughs
    VBForums Database Development FAQ
    My CodeBank Submissions: VB | C#
    My Blog: Data Among Multiple Forms (3 parts)
    Beginner Tutorials: VB | C# | SQL

  18. #18
    Super Moderator si_the_geek's Avatar
    Join Date
    Jul 2002
    Location
    Bristol, UK
    Posts
    39,434

    Re: String.Format

    A little addition to the class code:
    Code:
        Public Class ErrorCode1003
            Inherits ApplicationException
            Public Property strMessage As String
    
            Public Overrides Sub New(Message as String)
                strMessage = Message
            End Sub
        End Class
    ...allows you to simplify calling it, so instead of this:
    Code:
    Dim exCaseFailure As New ErrorCode1003
    exCaseFailure.strMessage = String.Format("{0} {1} StackTrace: {2}", glbstrMessage, vbNewLine, ex.ToString)
    ...you can do this:
    Code:
    Dim exCaseFailure As New ErrorCode1003(String.Format("{0} {1} StackTrace: {2}", glbstrMessage, vbNewLine, ex.ToString))
    ...or with string interpolation:
    Code:
    Dim exCaseFailure As New ErrorCode1003($"{glbstrMessage} {vbNewLine} StackTrace: {ex}")
    Assuming that you always want the message in that format, you could even alter the class to just take the values of glbstrMessage and ex (as separate properties, which could be set via Sub New), then it can return the entire string (including the vbNewLine and the text " StackTrace: ").

  19. #19
    PowerPoster techgnome's Avatar
    Join Date
    May 2002
    Posts
    31,277

    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
    * I don't respond to private (PM) requests for help. It's not conducive to the general learning of others.*
    * I also don't respond to friend requests. Save a few bits and don't bother. I'll just end up rejecting anyways.*
    * How to get EFFECTIVE help: The Hitchhiker's Guide to Getting Help at VBF - Removing eels from your hovercraft *
    * How to Use Parameters * Create Disconnected ADO Recordset Clones * Set your VB6 ActiveX Compatibility * Get rid of those pesky VB Line Numbers * I swear I saved my data, where'd it run off to??? *

  20. #20
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,166

    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.
    Nothing I post is production-ready. It is provided as-is, use it at your own risk.

  21. #21

    Thread Starter
    Hyperactive Member
    Join Date
    Dec 2011
    Location
    Oregon City, Oregon
    Posts
    342

    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?

  22. #22
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,166

    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.
    Nothing I post is production-ready. It is provided as-is, use it at your own risk.

  23. #23

    Thread Starter
    Hyperactive Member
    Join Date
    Dec 2011
    Location
    Oregon City, Oregon
    Posts
    342

    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.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width

Survey posted by VBForums.