Why to set an object to Nothing in the Finally block? - vb.net

In this VB.NET code:
Dim o as SomeClass
Try
o = new SomeClass
'call some method on o here
Catch(...)
...
Finally
o = Nothing
End Try
Why is there a need to set o to Nothing? What if i don't set it to Nothing in the Finally block? What i think is that it is OK if you don't set it to Nothing because the object will be marked for GC.

If the object is unsafe to use out of the try catch should this be done. If this was a stream for example you'd see the stream closed and then set to nothing. It is not always the right thing to do but this code is seen a lot.
consider this code
Sub Main()
Dim o As String
Try
o = "Hello"
Console.Out.WriteLine("hi {0}", o)
Catch ex As Exception
' do something here
Finally
o = Nothing
End Try
' unable to do something here
End Sub
Allthough it is a silly example it does mean you cannot reference o outside now because it is no longer set to an instance of an object. That is why it is done by a lot of people. If you are in a function and a function ends at that point there is no need to set to Nothing as the object falls out of scope however lots of people will set stuff to Nothing out of habit I'd consider that incorrect and bad code design

It's because the object is not safe to use outside the the try.. catch.. finally block. It's not guaranteed it's in a consistent state, so it's set to Nothing to make it obvious t's not supposed to be used.

Related

Variable declaration with Nothing check

Very (very) often we need to write stuff like
Dim Data = GetSomeData()
If Data IsNot Nothing Then
Data.DoSomething()
Else
...
End If
Maybe I am asking in vain but I am seriously hoping that VB.Net has some construct like:
IfExists Data = GetSomeData() Then
Data.DoSomething()
Else
...
End IfExists
In my dreams it does two important things:
No extra line for Nothing check
Variable A is not visible outside of the block and thus can't be used later on by mistake (just like "Using" or "With")
Is there anything similar to that that I haven't found yet?
Thanks!
EDIT:
Inspired by Bjørn-Roger Kringsjå's Answer I came up with something that would satisfy me (humbled by VB.Net's deficiencies):
<Extension()>
Public Sub IfExists(Of T)(This As T, DoIfNotNothing As Action(Of T), Optional DoIfNothing As Action = Nothing)
If This IsNot Nothing Then
DoIfNotNothing(This)
ElseIf DoIfNothing IsNot Nothing Then
DoIfNothing()
End If
End Sub
Then I can call it like this (with the false part being optional)
GetSomeData().IfExists(Sub(Data) Data.DoSomething())
or
GetSomeData().IfExists(Sub(Data) Data.DoSomething(), Sub() DoSomethingElse())
As stated by others and implied by me, it can't be done. Just like to share a 3'rd solution. This time we're going to use delegates.
No extra line for Nothing check
Variable A is not visible outside of the block and thus can't be used later on by mistake.
Implementation
Public Module Extensions
Public Sub IfExists(Of T)(testExpr As T, trueDlg As Action(Of T), falseDlg As Action)
If (Not testExpr Is Nothing) Then
trueDlg.DynamicInvoke(New Object(0) {testExpr})
Else
falseDlg.DynamicInvoke(New Object(-1) {})
End If
End Sub
End Module
Usage
IfExists(GetSomeData(),
Sub(A As Object)
'We have something (A)
End Sub,
Sub()
'We have nothing
End Sub
)
Shorter:
IfExists(GetSomeData(), Sub(A As Object)
'We have something (A)
End Sub, Sub()
'We have nothing
End Sub)
Or, the shortest version:
IfExists(GetSomeData(), Sub(A As Object) Debug.WriteLine(A.ToString()), Sub() Debug.WriteLine("Nothing"))
No, unfortunately there is nothing like that currently in VB.NET. The closest thing you could do to approximate that behavior would be to write a function like this:
Public Function Assign(ByRef target As Object, value As Object) As Boolean
target = value
Return (target IsNot Nothing)
End Function
Then you could use it like this:
Dim A As SomeType
If Assign(A, GetSomeData()) Then
' ...
Else
' ...
End If
But, as you pointed out, that doesn't really solve either of your stated problems. It's still an extra line of code, and the variable is still not scoped to only be accessible within the block where it was properly assigned.
The first point is not possible. There is no way to declare a variable and check it in a single statement.
The second point is sort of possible. Creating your own block scope is not supported by VB.NET, but you can achieve it by abusing other blocks. Insert your code within a single-use Loop While block:
Do
Dim A = GetSomeData()
If A IsNot Nothing Then
...
Else
...
End If
Loop While False
The Do block will be entered, with A declared local to that block, then the block will immediately exit at While False, and A will no longer be accessible.
A probably better way to simplify this is by restructuring the code into more, smaller methods so that the If ... Else is the entire method and there is no possibility of accidentally accessing an outdated variable later. When you do this, you can also exit early from the method in the simpler case instead of keeping both If and Else branches:
Dim A = GetSomeData()
If A Is Nothing Then
...
Exit Sub
End If
...
End Sub

Disposing data table?

I was reading this msdn article today and I am curious about finally part with disposing datatable. Is that really necessary? why should we dispose a datatable? If I exit the function or sub, is it not releasing the resources?
Explanation in the article is;
Dispose of the temporary data tables to release the resources.
Private Sub UpdateDB()
Dim deletedChildRecords As NorthwindDataSet.OrdersDataTable = _
CType(NorthwindDataSet.Orders.GetChanges(Data.DataRowState.Deleted), NorthwindDataSet.OrdersDataTable)
Dim newChildRecords As NorthwindDataSet.OrdersDataTable = _
CType(NorthwindDataSet.Orders.GetChanges(Data.DataRowState.Added), NorthwindDataSet.OrdersDataTable)
Dim modifiedChildRecords As NorthwindDataSet.OrdersDataTable = _
CType(NorthwindDataSet.Orders.GetChanges(Data.DataRowState.Modified), NorthwindDataSet.OrdersDataTable)
Try
If deletedChildRecords IsNot Nothing Then
OrdersTableAdapter.Update(deletedChildRecords)
End If
CustomersTableAdapter.Update(NorthwindDataSet.Customers)
If newChildRecords IsNot Nothing Then
OrdersTableAdapter.Update(newChildRecords)
End If
If modifiedChildRecords IsNot Nothing Then
OrdersTableAdapter.Update(modifiedChildRecords)
End If
NorthwindDataSet.AcceptChanges()
Catch ex As Exception
MessageBox.Show("An error occurred during the update process")
' Add code to handle error here.
Finally
If deletedChildRecords IsNot Nothing Then
deletedChildRecords.Dispose()
End If
If newChildRecords IsNot Nothing Then
newChildRecords.Dispose()
End If
If modifiedChildRecords IsNot Nothing Then
modifiedChildRecords.Dispose()
End If
End Try
End Sub
To answer your question, is it necessary to dispose a datatable? The concensus is no, here's my thought.
Setting things to null or nothing doesn't remove the memory used by the instance of the object. Neither does dispose. The GC does, when it runs. Remember, when you have a reference variable, you have a pointer to an object. When you repoint that variable to a null address, it doesn't delete the data at the previous address, nor does it free the memory.
Basically, dispose is useful for preparing your objects for being destroyed, but it doesn't actually release the memory used by the object. If you're dealing with resources that need cleaning up, like open streams, database connections, things like that, dispose is great.

Lambda doesn't close over object in With statement [duplicate]

Just thought I'd share this in case anyone else has run into this.
I did something similar today and it took me a while to figure out why this was causing a problem at runtime.
This code:
Public Class foo
Public bar As String = "blah"
End Class
Public Sub DoInline()
Dim o As New foo
Dim f As Func(Of String)
With o
f = Function() .bar
End With
Try
Console.WriteLine(f.DynamicInvoke())
Catch ex As Reflection.TargetInvocationException
Console.WriteLine(ex.InnerException.ToString)
End Try
End Sub
Throws a NullReferenceException. It seems as though the With is using the closure as its temp storage, and at the "End With", it sets the closure's variable to Nothing.
Here is that code in RedGate Reflector:
Public Shared Sub DoInline()
Dim o As New foo
Dim $VB$Closure_ClosureVariable_7A_6 As New _Closure$__1
$VB$Closure_ClosureVariable_7A_6.$VB$Local_VB$t_ref$L0 = o
Dim f As Func(Of String) = New Func(Of String)(AddressOf $VB$Closure_ClosureVariable_7A_6._Lambda$__1)
$VB$Closure_ClosureVariable_7A_6.$VB$Local_VB$t_ref$L0 = Nothing
Try
Console.WriteLine(RuntimeHelpers.GetObjectValue(f.DynamicInvoke(New Object(0 - 1) {})))
Catch exception1 As TargetInvocationException
ProjectData.SetProjectError(exception1)
Console.WriteLine(exception1.InnerException.ToString)
ProjectData.ClearProjectError
End Try
End Sub
Notice the
$VB$Closure_ClosureVariable_7A_6.$VB$Local_VB$t_ref$L0 = Nothing
Only "question" I can really ask is; is this a bug or a strange design decision that for some reason I'm not seeing.
I'm pretty much just going to avoid using "With" from now on.
This behavior is "By Design" and results from an often misunderstood detail of the With statement.
The With statement actually takes an expression as an argument and not a direct reference (even though it's one of the most common use cases). Section 10.3 of the language spec guarantees that the expression passed into a With block is evaluated only once and is available for the execution of the With statement.
This is implemented by using a temporary. So when executing a .Member expressio inside a With statement you are not accessing the original value but a temporary which points to the original value. It allows for other fun scenarios such as the following.
Dim o as New Foo
o.bar = "some value"
With o
o = Nothing
Console.WriteLine(.bar) ' Prints "some value"
End With
This works because inside the With statement you are not operating on o but rather a temporary pointing to the original expression. This temporary is only guaranteed to be alive for the lifetime of the With statement and is hence Nothingd out at the end.
In your sample the closure correctly captures the temporary value. Hence when it's executed after the With statement completes the temporary is Nothing and the code fails appropriately.
There's really only one bug that I see, the compiler should generate an error for this. Shouldn't be hard to implement. You can report it at connect.microsoft.com

What is the correct way to initialize objects in VBA?

I have a custom object that I was adding to an array via a loop. The problem was when I initialized the object like this:
Dim CallNum As New Lib_CallNum
The last object added in the loop would overwrite all the other objects added during the loop. So I would end up with an array filled with a bunch of the same objects. To fix this I had to change the way I was initializing the object to:
Dim CallNum As Lib_CallNum
Set CallNum = New Lib_CallNum
But I am unsure why the first initialization would not work. So what is the difference between the two sets of code?
The Dim inside a loop is not actually executed on each iteration. It is only executed the first time the variable is encountered.
To demonstrate this, add a section to your Lib_CallNum class initialisation definition:
Private Sub Class_Initialize()
Debug.Print "Initialise Lib_CallNum"
' ...
End Sub
and run your original code. Initialise will only be reported once. From then on you are adding the same instance to the array many times.
The correct way to initialise new instances objects is as #Doug has told you, Set ... = New ...
From my experience on your previous post, I still don't think you've got quite the right approach, although not sure since never saw your finished code. You should have something like:
Function Something
Dim CallNum as Lib_CallNum
...
Do While SomeCondition
Set CallNum = New Lib_CallNum
'do some stuff
Set CallNum = Nothing
Loop
Return Whatever
End Function
In other words, you should declare the object at the top of the function - not repeatedly in the loop - and instantiate (and set it to Nothing) in the loop.
If you google you can find explanations of why not to instantiate an object in its declaration. Professional Excel Development has a good one. But I think your problem might have been in never setting it to Nothing. Not sure though.

VB.Net - "With" and Closures don't mix

Just thought I'd share this in case anyone else has run into this.
I did something similar today and it took me a while to figure out why this was causing a problem at runtime.
This code:
Public Class foo
Public bar As String = "blah"
End Class
Public Sub DoInline()
Dim o As New foo
Dim f As Func(Of String)
With o
f = Function() .bar
End With
Try
Console.WriteLine(f.DynamicInvoke())
Catch ex As Reflection.TargetInvocationException
Console.WriteLine(ex.InnerException.ToString)
End Try
End Sub
Throws a NullReferenceException. It seems as though the With is using the closure as its temp storage, and at the "End With", it sets the closure's variable to Nothing.
Here is that code in RedGate Reflector:
Public Shared Sub DoInline()
Dim o As New foo
Dim $VB$Closure_ClosureVariable_7A_6 As New _Closure$__1
$VB$Closure_ClosureVariable_7A_6.$VB$Local_VB$t_ref$L0 = o
Dim f As Func(Of String) = New Func(Of String)(AddressOf $VB$Closure_ClosureVariable_7A_6._Lambda$__1)
$VB$Closure_ClosureVariable_7A_6.$VB$Local_VB$t_ref$L0 = Nothing
Try
Console.WriteLine(RuntimeHelpers.GetObjectValue(f.DynamicInvoke(New Object(0 - 1) {})))
Catch exception1 As TargetInvocationException
ProjectData.SetProjectError(exception1)
Console.WriteLine(exception1.InnerException.ToString)
ProjectData.ClearProjectError
End Try
End Sub
Notice the
$VB$Closure_ClosureVariable_7A_6.$VB$Local_VB$t_ref$L0 = Nothing
Only "question" I can really ask is; is this a bug or a strange design decision that for some reason I'm not seeing.
I'm pretty much just going to avoid using "With" from now on.
This behavior is "By Design" and results from an often misunderstood detail of the With statement.
The With statement actually takes an expression as an argument and not a direct reference (even though it's one of the most common use cases). Section 10.3 of the language spec guarantees that the expression passed into a With block is evaluated only once and is available for the execution of the With statement.
This is implemented by using a temporary. So when executing a .Member expressio inside a With statement you are not accessing the original value but a temporary which points to the original value. It allows for other fun scenarios such as the following.
Dim o as New Foo
o.bar = "some value"
With o
o = Nothing
Console.WriteLine(.bar) ' Prints "some value"
End With
This works because inside the With statement you are not operating on o but rather a temporary pointing to the original expression. This temporary is only guaranteed to be alive for the lifetime of the With statement and is hence Nothingd out at the end.
In your sample the closure correctly captures the temporary value. Hence when it's executed after the With statement completes the temporary is Nothing and the code fails appropriately.
There's really only one bug that I see, the compiler should generate an error for this. Shouldn't be hard to implement. You can report it at connect.microsoft.com