Method Calls on Objects that Aren't "Guaranteed" to be Instantiated - vb.net

I recently wrote a section of code wherein upon finishing, I received a warning from the compiler telling me that one of my variables is being used before it is assigned a value. In practice, the method call on this object will never be made without the object being instantiated beforehand. Here is the snippet of code
Try
fs = New FileStream(fileName, FileMode.Open)
Dim PolarMatrix As PolarMatrix
PolarMatrix = DirectCast(bf.Deserialize(fs), PolarMatrix)
fs.Close()
Return TypeOfStructure.Polar
Catch ex As Exception
fs.Close() 'Warning on this line: Variable 'fs' is used before it has been assinged a value
End Try
I assume I'm receiving this warning because the first line in the Try section may be the line to throw the error, and the Object will never be instantiated. FileName though, is a variable being passed to this method which has already been checked for errors, so it is guaranteed to be correct. The error I'm expecting to perhaps be thrown comes during the deserialization.
So my question: When warnings are given on objects that the compiler thinks may not have been instantiated, does this overrule the user knowing that a problem will never arise on that line? Is it sometimes necessary to add code simply to appease the compiler? Or is what I've done here bad practice?

How are you sure that fs will always be instantiated, even if the filename is correct, it could still fail to open for many other reasons.
However, the easy solution to this problem would be to get rid of the catch completely and use a Using statement instead, as:
Try
Using fs = New FileStream(fileName, FileMode.Open)
Dim PolarMatrix As PolarMatrix
PolarMatrix = DirectCast(bf.Deserialize(fs), PolarMatrix)
Return TypeOfStructure.Polar
End Using
Catch ex as Exception
' do something here
End Try
This means that the fs will be automatically disposed of when this section of code exists, and Stream.Dispose closes the stream.
And to answer your actual question, sometimes the compiler is wrong, and you will have to either ignore the warnings or add some extra code to make them go away, but in general, assume that the compiler is correct until you're absolutely sure that that's not the case.

Related

Understanding the Open method and the Try block in VB.NET

I'm using VB.NET for the first time, to check if a file is in use, but there are some lines of code that I don't fully understand.
Can someone explain the two lines of code highlighted below in the comments?
Public Sub Main()
IsFileInUse("C:\someFolder\file.pdf")
End Sub
Function IsFileInUse(filePath As String) As Boolean
IsFileInUse = False
If System.IO.File.Exists(filePath) Then
Dim fileInfo As System.IO.FileInfo
Dim stream As System.IO.FileStream
fileInfo = New System.IO.FileInfo(filePath)
Try
' Can someone explain this line of code?
' how does this determines where to go from here, Catch or Finally?
stream = fileInfo.Open(System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite, System.IO.FileShare.None)
Catch
IsFileInUse = True
MessageBox.Show("It looks like the file is opened")
Finally
If stream IsNot Nothing Then
' What is this closing?
stream.Close()
End If
End Try
Else
MessageBox.Show("File does NOT Exist")
End If
End Function
how does this determines where to go from here, Catch or Finally?
Look at the documentation for FileInfo.Open. The Exceptions section shows all of the possible exceptions that can happen.
If an exception is thrown, the Catch block will be executed.
The Finally block always gets executed, whether or not an exception was thrown.
What is this closing?
It will release the stream's resources, in this case it will close the file.
The Try block runs the code. In the Try block, a stream is used to open the file and access its contents. If that code errors for any reason, it will throw an Exception, which will then cause to the Catch block to be run. The Finally block of the code will run whether an Exception is thrown or not. In the Finally block, the stream to the File is being closed.
The code is determining whether or not the file is currently "in use" by any other processes by attempting to open the file with read/write access. Whether the opening of the file fails or not, it always closes the file stream. It assumes that if opening the file in that mode fails for any reason, then it must be because it is "in use". That's a bad assumption to make, and likely not the best way to accomplish it anyway, but for what it's worth, that's what it's doing.
The Try/Catch block is VB.NET's preferred syntax for exception handling (it replaces the older On Error syntax which predated .NET). When anything inside of the Try section throws an exception, execution will jump to the Catch section. Once execution of the Catch section completes, it then jumps to the Finally section. If nothing in the Try section throws an exception, then once it's done, it also jumps to the Finally section. Essentially, everything in the Finally section is "guaranteed" to execute, whether or not an exception occurred, whereas the code in the Catch section only executes when there is an exception.
In other words, consider this example:
' Outputs "ABCE" (does not output "D")
Console.Write("A")
Try
Console.Write("B")
Console.Write("C")
Catch
Console.Write("D")
Finally
Console.Write("E")
And compare it to this example:
' Outputs "ABDE" (does not output "C")
Console.Write("A")
Try
Console.Write("B")
Throw New Exception()
Console.Write("C")
Catch
Console.Write("D")
Finally
Console.Write("E")
See the MSDN article for much more information on the topic.

Using a IO variable before it has been assigned a value in a Try...Catch...Finally

I'm trying to use an IO object in VB.net which I'm not initialising from the declaration point. I use the object in several places in a Try..Catch block and then I use it again in the Finally block. For the purpose of my question you can ignore the method calls of my code. VS is showing this warning: Variable 'objSourceDirectory' is used before it has been assigned a value. A null reference exception could result at runtime.
I believe having safe checks in some places in my code the null reference shouldn't happen, but how could I get rid of this warning? I would initialise the variable as empty at declaration point but this can't be possible with IO.DirectoryInfo, as it doesn't have a construction without parameters. How can I fix this?
Dim object1 As IO.DirectoryInfo
Try
mlstProcessedFiles = New Global.System.Collections.Generic.List(Of String)
' Gather Data from import source
object1 = New IO.DirectoryInfo(object2.Location)
MethodD(object2.Location)
MethodA(object1)
MethodC(object2.Location)
' Work through files found in the source. Detect if the file was moved and add it to the manual processing
For Each objFile In object1.GetFiles()
Try
MethodB(objFile, object2)
objFile = Nothing
Catch objFileEx As FileNotFoundException
Catch objDirEx As DirectoryNotFoundException
MethodX(mstrERROR_11_MOVED, objDirEx.Message)
Continue For
End Try
Next
Catch objectException As Exception
Throw
Finally
'Make sure processed files have been moved to their folders
Method1()
Method2()
Method3(object1)
End Try
You haven't shown objSourceDirectory in your code. I assume it is object1 which is used in the Finally.
Yes, you cannot use a variable when it is possible that this variable was not assigned a value. You are using it in the Finally which is executed always, even on error.
Since you haven't assigned a default value it is possible that you get an exception which causes it to remain unassigned. This compiler error prevents you from careless mistakes.
To fix it you could assign Nothing:
Dim object1 As IO.DirectoryInfo = Nothing
but you should check if it's Nothing in the Finally or in Method3.
In this case you could also move the initilization to the declaration which makes it definitely assigned:
Dim object1 As IO.DirectoryInfo = New IO.DirectoryInfo(object2.Location)
Note that the compiler is not smart enough to check if it is possible that an errors is raised before the variable is initialized. It just looks at the Try and "thinks": it's possible that here comes an error.

VB can't string to arraylist when read from file

Either I'm missing something really obvious or something about vb is really messed up. I'm trying to read in from a file and add the lines to an arraylist... pretty simple If I add strings to the arraylist this way
selectOptions.Add("Standard")
selectOptions.Add("Priority")
selectOptions.Add("3-Day")
selectOptions.Add("Overnight")
I have no problems
But when I do this it appears to end up empty which makes no sense to me.
Dim reader As StreamReader = My.Computer.FileSystem.OpenTextFileReader(path)
Dim line As String
Do
line = reader.ReadLine
selectOptions.Add(line)
Loop Until line Is Nothing
reader.Close()
Messagebox.show line all day so I know it is reading the file and the file isn't empty and I have checked the type of line which comes back as string. This makes no sense to me.
Checking for reader.EndOfStream in a While loop will probably work better:
Dim reader As New StreamReader(path)
Dim line As String
While Not reader.EndOfStream
line = reader.ReadLine
selectOptions.Add(line)
End While
reader.Close()
You can also get an exception if selectOptions isn't declared as a New ArrayList, if you properly have all your Options turned On.
Another thing to remember, if your code is in the form's Load Handler, it won't throw an exception it will just break out of the handler routine and load the form. This makes it really hard to find things like bad file names, badly declared objects, etc.
One thing I do is put suspect code in a button's Click handler and see what exceptions it throws there.
Of course this could all be moot if you use the File.ReadAllLines method and add it directly to the ArrayList:
selectOptions.AddRange(File.ReadAllLines(path))

VB.NET: Nesting "Using" for database access

I'm wondering if this is a good way of doing data access, in terms of all the database objects being properly closed and disposed? For example:
Using conn As New SqlConnection(MyConnectionString)
Using cmd As New SqlCommand("some SQL here", conn)
... add parameters ...
conn.Open()
Using dr As SqlDataReader = cmd.ExecuteReader()
While dr.Read()
... do stuff ...
Wend
End Using
End Using
End Using
Is nesting Using like acceptable practice? If I exit the method at some point within the Read() loop, will the use of Using like this ensure all objects are cleaned up properly regardless?
Using guarantees orderly disposal in an implicit try/finally block.
' THE FOLLOWING TRY CONSTRUCTION IS EQUIVALENT TO THE USING BLOCK
Dim resource As New resourceType
Try
' Insert code to work with resource.
Catch ex As Exception
' Insert code to process exception.
Finally
' Insert code to do additional processing before disposing of resource.
resource.Dispose()
End Try
Nested usings work in a similar fashion. If you exit a block of code, it will execute the finally block, and properly dispose your objects.
http://msdn.microsoft.com/en-US/library/htd05whh(v=VS.80).aspx
To add, the Using block will "behind the scenes" add a Try Finally statement. In the finally statement it will call IDisposable.Dispose on the object. In other words, no matter what you do or what happens, the object will get disposed.
Yes, this is ok. The Dispose method of IDisposable objects is always called.
PS: In this case, the Dispose method also contains the Close method.
You are writing VB.Net, so this only partially applies, but for the C# folks out there using StyleCop, multiple using statements like this will cause exception 2202 in StyleCop.
There is a lengthy section at the msdn link of dissenting comments as to the usefulness of this rule in stylecop.
I will not make a judgment call as to whether you should heed StyleCops warnings for C# in your VB.Net code.

Should you call close even within a Using block

Using sr As New System.IO.StreamWriter("C:\test.txt")
sr.WriteEnd
End Using
Using sr As New System.IO.StreamReader("C:\test.txt")
sr.ReadToEnd
End Using
This is equivalent to:
sr = New System.IO.StreamWriter("C:\test.txt")
sr.WriteEnd
sr.Dispose
sr = New System.IO.StreamReader("C:\test.txt")
sr.ReadToEnd
sr.Dispose
Am I right in saying the attempt to read the file may fail if the Garbage collector had not got around to disposing the StreamWriter and closing the file. If so where is the benefit is using the Using Statement in this case. Is is better practice to use
Using sr As New System.IO.StreamWriter("C:\test.txt")
sr.WriteEnd
sr.close()
End Using
It is a best practice to always the Using statement because if an exception is thrown during the reading the stream might never be disposed and your application will leak a file handle. If this is a desktop application the process will probably die and release the handle but in a web application not releasing unmanaged resources such as file handles could be catastrophic.
Also you don't need to call Close on your stream if you are calling Dispose.
Remark: if you want to read the contents of a text file into a string you could directly do this instead of going through the pain of instantiating a StreamReader, reading it to the end and disposing it:
Dim data = File.ReadAllText("C:\test.txt");
No, the Garbage Collector does not dispose the object, calling Dispose does that, and the using block makes sure that happens. The object may still be in memory, but the file will have been dealt with.
If you didn't call close or dispose (either manually or by use of the using block), and the object got collected it would then be finalised (slowing up garbage collection) and that would close the file handle. But this last-chance is different to dispose.
You don't have to call close on the file within the using block, but you are free to. This may be a good idea to release the handle quickly (not often an issue with file handles, more often an issue with pooled database connections), with the using block giving you an added guarantee (it's always safe to call dispose, even if its already been called).
Actually, it is equivalent to this: The Finally blocks will guarantee the Dispose is called and everything is cleaned up in the event of an exception.
Try
sr = New System.IO.StreamWriter("C:\test.txt")
sr.WriteEnd()
Finally
sr.Dispose()
End Try
Try
sr = New System.IO.StreamReader("C:\test.txt")
sr.ReadToEnd()
Finally
sr.Dispose()
End Try
Think if you are done and don't care too much about it after, can let the dispose just happen
BUT dispose may take a while, so if you need to use the file (e.g. pass to another app, move to new location, or any other use of the file), then probably should Close it explicitly
Close should release any locks, expect this will fail:
dim fn as string = "C:\temp.txt"
Using sr As New System.IO.StreamWriter(fn)
sr.WriteLine("Junk")
' sr.close
End Using
system.io.file.delete(fn)
Expect the delete to throw an error, since file dispose may not yet have happened
If you explicitly close (e.g. un-comment the close), then should always succeed