NullReferenceException and/or variable usage before it has been assigned - vb.net

I am new to .net and am doing my best to understand the Nuances (or Nuisance). I am trying to print text on a form. I did find many examples of the Graphics class, DrawString() etc. Unfortunately, I cannot get any of them to work. The stumbling block for me seems to be the error “NullReferenceException” and/or the warning “variable usage before it has been assigned”. One example found at "http://msdn.microsoft.com/en-US/library/76c5db29(v=vs.80).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1". vs2005 displays the warning "Variable 'myE' is used before it has been assigned a value. A null referene excpetion could result at runtime and sure enough when I run a NullReferenceException is thrown at "e.Graphics.DrawString(drawString, drawFont, drawBrush, drawPoint)" How do I fix this?
Public Class Form1
Public Sub DrawStringPointF(ByVal e As PaintEventArgs)
Dim drawString As [String] = "Sample Text"
Dim drawFont As New Font("Arial", 16)
Dim drawBrush As New SolidBrush(Color.Black)
Dim drawPoint As New PointF(150.0F, 150.0F)
e.Graphics.DrawString(drawString, drawFont,drawBrush, drawPoint)
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim myE As PaintEventArgs
DrawStringPointF(myE)
End Sub
End Class

You can't do that.
Instead, handle the Paint event and draw everything you need to.
To trigger a redraw, call Invalidate().

Your question is broader than your problem.
You're asking what a null reference exception is; this question has been answered many times elsewhere. A NRE (NullReferenceException) is referring to a variable that isn't defined at all
We can think about memory as being a vast, open parking lot. When you define a variable, using a statement like this,
Dim myObject as Integer
... you're claiming one or more of the parking spaces in the parking lot. However, all you've done is declare that this spot is yours, but not what goes in it. To give it a value, simply use the equals operator.
myObject = 100
Simple objects, known as primitives, like strings, numbers and characters, have an implied value when they're not defined. For example, integers are assumed to be zero if they have no value.
In your program, when you get a NRE, mouse over each argument in the function and the object that the function is being called from. If any of them claim to have a value of Nothing, there's your problem. You've reserved space for your object, but not yet given it a value.
As SLaks said, your specific problem is related to how you're calling the method, but without a strong understanding of what Nothing means, it may not correctly address the underlying question>
Finally, let's look at code that's specifically yours. You call
Dim myE as PaintEventArgs.
In this statement, you have reserved space in the memory- that is to say, claimed a parking space we talked about earlier. However, it does NOT have a value! It's empty. The program cannot function properly because you're telling it to perform an action on something that doesn't exist. To fix it, use the New operator. When making new objects, first reserve them in memory (Dim actually stands for "define in memory") and then give them a value with the New operator. You'd see something like this:
Dim myObj As Object
myObj = New Object()
You can also do it inline:
Dim myObj As Object = New Object()
When you try to do that with your PaintEventArgs, you'll see that that's quite impossible. PaintEventArgs requires information that's taken from a Windows Form control. That method you've written needs to handle an event. This post is too long as it is, so hopefully someone else can explain events and how they're related to methods. Basically, each control (textbox, image, or even the picture itself) raises an event every time it "paints" to the screen. You can "handle" that event; or do actions when it occurs, by doing this:
Public Sub OnPaint(sender as Object, e as PaintEventArgs) Handles myControl.OnPaint
You are declaring that whenever myControl raises an event for OnPaint, it will call this method, with arguments provided from the control.
You cannot simply substitute the required arguments from elsewhere. In order for it to work properly, the event arguments must be passed from the control that you wish to paint to.
I hope this helps. Please let me know if I've been unclear.

Related

What exactly are parentheses around an object reference doing in VB.NET?

I have some procedures that take Control object references as a parameter.
I have a bunch of Controls throughout my project of varying derived types such as Button, TextBox, PictureBox, ListBox, etc.
I was calling the procedure and passing the reference as normal:
Procedure(controlRef)
I changed some of the Warning Notifications in my project configuration. I'm guessing it was changing the Implicit Conversion Notification from 'None' to 'Warning' that caused warnings similar to the following to appear everywhere these procedures were called:
"Implicit conversion from 'Control' to 'Button' in copying the value of 'ByRef' parameter 'parControl' back to the matching argument."
This makes sense, I'm doing an Implicit Conversion, but hang on a second, I'm passing a Button in to a Control parameter, not a Control to a Button like it says, I'm slightly confused what's happening here.
Anyway, I take a look at the "Show potential fixes" and there is no fix suggestion, only Suppress or Configure options, okay. So I do a explicit cast using DirectCast(controlRef, Control) to see if that'll remove the warning on Implicit Conversion, which it does, but it gets replaced by a Redundant Cast warning, again, this makes sense. So I remove the cast using the potential fixes and the argument in the procedure call is left with parentheses around it and no more warnings.
Procedure((controlRef))
What is going on here exactly?
Since the signature for Procedure is Sub Procedure(ByRef param As Control) and you're passing a Button to the method, the compiler is correctly warning you about an implicit conversion.
Imagine that this were the definition of Procedure:
Sub Procedure(ByRef param As Control)
param = New Label()
End Sub
And if you called it this way:
Dim button = New Button()
Procedure(button)
Then you're effectively calling this code:
Dim button As Button = New Button()
button = New Label()
Hence the compiler warning.
If you change the signature to Sub Procedure(ByVal param As Control) then there is no possibility of assignment back to the calling variable and the warning will go away.
The use of the extra parenthesis forces the call to be ByVal instead of ByRef. See https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/procedures/how-to-force-an-argument-to-be-passed-by-value
This is not an answer to the question but it may be a solution to the actual problem. It also requires significant code, so I decided an answer was the best option.
One has to wonder why you have declared that parameter ByRef in the first place. Many people do so when it is not required because, as in VB6, they think that it will prevent an object being copied. That is not the case because reference type objects, i.e. class instances, don't get copied when passed by value anyway. That's the whole point of reference types, i.e. the value of a variable is a reference, not an object, so passing by value only copies the reference, not the object. If you are not assigning anything to that parameter inside the method then it should be declared ByVal.
If you are assigning to the parameter inside the method then the solution is to declare the method to be generic. That way, the parameter won't be Control but will actually be the type you pass in. In its simplest form, that would be:
Private Sub Procedure(Of T)(ByRef control As T)
'...
End Sub
That's probably not enough though, because that would allow you to pass any object as an argument. To restrict the method to only accept controls:
Private Sub Procedure(Of TControl As Control)(ByRef control As TControl)
'...
End Sub
Now you will only be able to pass a control to the method but, inside the method, the parameter will be treated as the actual type of the argument you passed, e.g. if you pass a Button then TControl is fixed to be Button. If you need to create a control of the appropriate type inside the method then you need another restriction too, which enables you to assume a parameterless constructor, e.g.
Private Sub Procedure(Of TControl As {New, Control})(ByRef control As TControl)
control = New TControl With {.Location = New Point(100, 100),
.Size = New Size(50, 25)}
'...
End Sub
That code means that, inside the method, you know that the type of the parameter is Control or derived from that type and that you can create new instances by invoking a parameterless constructor.

Value of type 'string' cannot be converted and property is readonly

Already asked this question in bitcoin stackexchange but it may not get answered for days or maybe never so trying here and its related to something basic I need to do related to initialization of variables in vb.net
Maybe noob question but important for me to understand how to pass value from a textbox or string to something else which is 'bob.PubKey' in this case
Imports NBitcoin
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim bob As New Key
Dim alice As New Key
Dim satoshi As New Key
Dim redeemScript As Script = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(2, {bob.PubKey, alice.PubKey, satoshi.PubKey})
Label1.Text = redeemScript.Hash.GetAddress(Network.TestNet).ToString
'Console.WriteLine(bob.PubKey)
End Sub
End Class
NBitcoin: https://github.com/MetacoSA/NBitcoin
EDIT-------------------------------------------------
Hrm... nope. Don't do that.
Changing the PubKey is not straightforward. The PubKey property has only a Getter, no Setter, so you can't just spawn a new one with the content you want, and changing the existing one's content isn't an option either.
I may be wrong as I'm not used to nBitCoin, but it seems to me that you're not supposed to switch the pubKey on the fly. Your only option would be to instantiate a new Key with your brand new PubKey, but the new key won't automatically share all the properties of the current key and thus could cause unexpected behavior.
The PubKey is created automatically when you need it, and it's value is not supposed to be messed with.
(There may be a way to do it through the class in the Secp256k1 folder, but this is deep enough for me to say that there must be a better way to tackle this issue.)
Original answer which was the wrong way around
You can normally use an assignment operator for this kind of operation, like Textbox1.text = bob.PubKey, but the problem is that bob is a Key class, not a String, and .PubKey is a PubKey class, not a String either.
I'm not acquainted with NBitcoin but I'm willing to bet that you can do this:
Textbox1.text = bob.PubKey.ToString()
and it'll show the key, or at least a String representation of the key. The thing is, this may not be usable in other contexts, not in the same way that you can use the whole bob object.
I just took a look at the source code and it seems that it should work just as I guessed. It should be the exact same result than doing
Textbox1.text = bob.PubKey.ToHex()
Have fun!

Saving Database issue

So basically, I believe I am using the correct code yet the database will still not update. It will work for the current session, however, when I stop and restart the program, it appears that the data has not been updated in the database.
The really interesting part is that I am using the same method to update the database elsewhere, which when used and session restarted, the database has been updated.
p.s. I also have the same adapters and binding sources set up etc on both forms
I am so confused, help pls
Code that I believe is correct but is not working: (updating on another form so I have one place where all forms update hence FRMMain. etc)
Private Sub btnConfirm_Click(sender As Object, e As EventArgs) Handles btnConfirm.Click
Dim CurrentPoints As Integer
Dim UpdatedPoints As Integer
CurrentPoints = FRMMain.MyDBDataSet.Tables("TBLPupil").Rows(looopcount)(15)
UpdatedPoints = CurrentPoints + stfPoints
FRMMain.MyDBDataSet.Tables("TBLPupil").Rows(looopcount)(15) = UpdatedPoints
FRMMain.TBLPupilTableAdapter.Update(MyDBDataSet.TBLPupil)
FRMMain.TBLPupilTableAdapter.Fill(MyDBDataSet.TBLPupil)
End Sub
Code that I am using in another form that that DOES work:
Private Sub BtnYes_Click(sender As Object, e As EventArgs) Handles BtnYes.Click
Dim Points As Integer = FRMPupil.Pointss
Dim Cost As Integer = FRMPupil.RewardCost
Points = Points - Cost
FRMPupil.LePoints = Points
MyDBDataSet.Tables("TBLPupil").Rows(FRMLogin.DBLocation)(15) = Points
FRMMain.TBLPupilTableAdapter.Update(MyDBDataSet.TBLPupil)
FRMMain.TBLPupilTableAdapter.Fill(MyDBDataSet.TBLPupil)
Me.Hide()
End Sub
My code is correct but is not working.
No, if it is not working, then it is not correct!
There are different things you can do: DRY, Dont Repeat Yourself. You are repeating the code for updating points at several places in your code. This is error prone. Write it once and re-use it, e.g. by applying the the Repository Pattern. It makes it easier to detect errors and correct them. It allows you to re-use code that has already been tested in other scenarios (on another form).
Debug, debug, debug. Place breakpoints in the not working methods and see what happens. Do all the variables have the expected values? E.g., does looopcount have the same value as FRMLogin.DBLocation? There must be a difference somewhere. See: Navigating through Code with the Debugger or the more recent article Debug your Hello World application with Visual Studio 2017.

Difference between adding zero to nothing in Visual Basic.Net?

Why is this different?!
Public Class Form1
Public Function MyFunction() As Integer?
Return Nothing
End Function
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim o As Object = Me
MsgBox(TypeName(Me)) ' Form1
MsgBox(TypeName(o)) ' Form1
MsgBox(TypeName(Me.MyFunction())) ' Nothing
MsgBox(TypeName(o.MyFunction())) ' Nothing
' but
MsgBox(TypeName(Me.MyFunction() + 0)) ' Nothing
MsgBox(TypeName(o.MyFunction() + 0)) ' Integer
End Sub
End Class
Using Option Strict On is a pretty good way to avoid surprises like this. You'll get a "what the heck are you trying to do?" error message from the compiler.
But with it Off, these are valid statements, executed by the DLR, the Dynamic Language Runtime. Which is capable of evaluating late-bound expressions like this. It however has a problem with a nullable type like Integer?. It needs to deal with the boxed version of the value. Which is just plain Nothing. And Nothing doesn't have any type information associated with it. There's nothing the DLR can do to see that this started life as a nullable integer, for all it knows it could be a string that is Nothing.
The compiler cannot help either, it cannot emit any code to make the expression follow normal evaluation rules. All it knows is that there is some function, it doesn't know which, whose name is "MyFunction" with no idea what kind of value it returns. It passes the buck to the DLR to sort it out.
So the DLR just punts at it. And it comes up with "No idea" + 0 = 0. Given that it does have type information for 0. It is an Integer so it tries to interpret the left operator as an integer as well. Which is valid, Nothing is a correct default value for Integer.
Feature, not a bug.
Visual Basic .NET had Nothing long before it had nullable value types - it inherited it from pre-.NET Visual Basic. And in some cases, it behaves more like C#'s default(T) then t does null.
Your final call is invoking the AddObject method in the Visual Basic compiler services. This method has existed for a long time, and again pre-dates nullable value types, and unfortunately isn't well documented.
Unfortunately, they couldn't make nullable types behave absolutely consistently, especially in the face of late-bound calls, whilst still maintaining backwards compatibility. For instance, this also prints 0:
Console.WriteLine(CType(CType(Nothing, Object), Int32))

Why is it legal to pass "Me" ByRef in VB.NET?

I was shocked just a moment ago to discover that the following is legal (the C# equivalent is definitely not):
Class Assigner
''// Ignore this for now.
Public Field As Integer
''// This part is not so weird... take another instance ByRef,
''// assign it to a different instance -- stupid but whatever. '
Sub Assign(ByRef x As Assigner, ByVal y As Assigner)
x = y
End Sub
''// But... what's this?!?
Sub AssignNew()
''// Passing "Me" ByRef???
Assign(Me, New Assigner)
End Sub
''// This is just for testing.
Function GetField() As Integer
Return Me.Field
End Function
End Class
But what's even stranger just as strange to me is that it doesn't seem to do what I expect:
Dim a As New Assigner With {.Field = 10}
a.AssignNew()
Console.WriteLine(a.GetField())
The above outputs "10," not "0" like I thought it would (though naturally, this expectation was itself infused with a certain kind of horror). So it seems that you can pass Me ByRef, but the behavior is somehow overridden (?) by the compiler to be as if you had passed Me ByVal.
Why is it legal to pass Me ByRef? (Is there some backwards-compatibility explanation?)
Am I correct in saying that the behavior of doing this is overridden by the compiler? If not, what am I missing?
This behavior actually follows pretty directly from the Visual Basic specification.
11.4.3 Instance Expressions
An instance expression is the keyword Me, MyClass, or MyBase. An instance expression, which may only be used within the body of a non-shared method, constructor, or property accessor, is classified as a value.
9.2.5.2 Reference Parameters
If the type of the variable being passed to a reference parameter is not compatible with the reference parameter's type, or if a non-variable is passed as an argument to a reference parameter, a temporary variable may be allocated and passed to the reference parameter. The value being passed in will be copied into this temporary variable before the method is invoked and will be copied back to the original variable (if there is one) when the method returns.
(All emphasis mine)
So, the compiler will create a temporary variable assigned to the value of Me to be passed as the ByRef parameter. Upon return, no copy of the resulting value will take place since Me is not a variable.
It appears the compiler transforms "Me" into a variable which is then passed ByRef. If you compile your code, then open it with Reflector, you can see what's happening:
Class Assigner
''// Methods
Public Sub Assign(ByRef x As Assigner, ByVal y As Assigner)
x = y
End Sub
Public Sub AssignNew()
Dim VB$t_ref$S0 As Assigner = Me
Me.Assign((VB$t_ref$S0), New Assigner)
End Sub
Public Function GetField() As Integer
Return Me.Field
End Function
''// Fields
Public Field As Integer
End Class
So it looks like when you call AssignNew(), you are assigning the new instance to the internally generated variable. The "a" variable doesn't get touched because it's not even a part of the function.
This is just one of the thousands of possible 'almost errors' a programmer can make. MS caught most of them, in fact, sometimes I'm suprised at how many warnings do come up.
they missed this one.
As far as why it doesn't change 'me', it's a darn good thing! When you use 'me', it just passes a copy of the real class you are working with, for safety purposes. If this worked they way you were hoping, we would be talking GIANT side-effect. You're innocently working away with in your class' methods, and them BAM all of a sudden you are in an ENTIRELY different object! That would be awful! If you're going to do that, you might as well just write a piece of spagetti MS-Basic line-numbered code with all globals that get randomly set, and no subs/functions.
The way this works is the same way if you pass arguments in parenthesis. For example this works as expected:
Assign(Reference_I_Want_To_Set, New Assigner)
But this doesn't change anything:
Assign((Reference_I_Want_To_Set), New Assigner)
If you reflect the above type of code as adam101 suggests you will see similar results. While that is huge frustration with the parenthesis, it is a very good thing with Me !!!
what you need to do to make this code work is this:
Class Assigner
''// Ignore this for now.
Private newPropertyValue As Integer
Public Property NewProperty() As Integer
Get
Return newPropertyValue
End Get
Set(ByVal value As Integer)
newPropertyValue = value
End Set
End Property
''// This part is not so weird... take another instance ByRef,
''// assign it to a different instance -- stupid but whatever. '
Shared Sub Assign(ByRef x As Assigner, ByVal y As Assigner)
x = y
End Sub
''// But... what's this?!?
Shared Sub AssignNew(ByRef x As Assigner)
''// Passing "Me" ByRef???
Assign(x, New Assigner)
End Sub
End Class
then use it like
Dim a As New Assigner With {.NewProperty = 10}
Assigner.AssignNew(a)
my understanding is you cannot change the reference of the object while using it, so you need to change it in a shared sub
since Me cannot be the target of an assignment, the code seem to create a copy of it and from that point on, your not using the real object, but a copy of it