What exactly are parentheses around an object reference doing in VB.NET? - 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.

Related

vb.net How can I pass a reference to a structure from one thread to two other threads? [duplicate]

I get a build error when I try to pass a structure reference into a thread.
dim antenna_frame_buffer as Antenna_Frame_Buffer_structure
...
new_buffer_write_thread = new Thread( AddressOf frame_buffer_write_Thread )
new_buffer_write_thread.Start( antenna_frame_buffer )
...
sub frame_buffer_write_Thread( ByRef antenna_frame_buffer as Antenna_Frame_Buffer_structure )
...
THE ERROR...
Severity Code Description Project File Line Suppression State
Error BC30518 Overload resolution failed because no accessible 'New'
can be called with these arguments:
'Public Overloads Sub New(start As ThreadStart)': Method 'Public Sub frame_buffer_write_Thread(ByRef antenna_frame_buffer As
Embedded_Communication_Interface.Antenna_Frame_Buffer_structure)' does
not have a signature compatible with delegate 'Delegate Sub
ThreadStart()'.
'Public Overloads Sub New(start As ParameterizedThreadStart)': Method 'Public Sub frame_buffer_write_Thread(ByRef
antenna_frame_buffer As
Embedded_Communication_Interface.Antenna_Frame_Buffer_structure)' does
not have a signature compatible with delegate 'Delegate Sub
ParameterizedThreadStart(obj As Object)'. SYS HUB and HW
GUI C:\PRIMARY\WORK\SYSTEM
HUB\SOURCE\Embedded_Communication_Interface.vb 1030 Active
You can't. You're not actually calling that method directly anyway, so how could the ByRef parameter be of use? You're calling the Thread.Start method and it doesn't have a ByRef parameter, so you couldn't get the value back that way. That's even ignoring the fact that Thread.Start returns immediately and you don't know when the method it invokes will return, so you couldn't know when the modified value was available anyway. In short, ByRef parameters don't make sense in such a context so don't try to use them.
EDIT:
You may be able to use a Lambda expression that calls your method as the delegate when you create the thread and then you will be able to get the code to run:
new_buffer_write_thread = New Thread(Sub() frame_buffer_write_Thread(antenna_frame_buffer))
new_buffer_write_thread.Start()
I don't think that that will ever return the parameter value after the method completes to the original variable though and, if it did, you'd not know when it did so because you don't know when the method completed, which is exactly why it shouldn't happen at all. I think that LINQ creates a closure that shields the original variable from changes via that parameter, even though it appears like they'd be linked.
Structure cannot be passed by reference to a thread.
However, and fortunately, an object of a class CAN be passed by reference.

Calling Public variable from userform

In a userform, I have this at the top:
Public DelMonth As Variant
The value of DelMonth is read from a ComboBox, and I can call it from different subroutines within that userform just fine. But when I call it from a separate module, it doesn't read it. It doesn't even throw an error. If I do a MsgBox DelMonth, it doesn't do anything.
A form is an object; a public field in an object module belongs to an instance of that object. UserForms are little more than class modules with a default instance (i.e. a VB_PredeclaredId = True attribute) and a designer.
If you're using the form's default instance (a rather bad idea), then you can do this:
MsgBox UserForm1.DelMonth
Note that storing state in global objects is bug-prone, and will end up causing issues.
If you're treating the form like the full-fledged class it is, then you'll have something like this:
With New UserForm1
.Show
MsgBox .DelMonth
End With
Note that the field being Public means anyone, anywhere can go and write to it. What you mean is for the form to determine its value, and for the caller to be able to read that value. You do this by encapsulating the field with a Property Get member - start by making the field Private:
Option Explicit
Private DelMonth As Variant ' wouldn't Integer or Long be more appropriate?
Public Property Get DeliveryMonth() As Long
DeliveryMonth = DelMonth
End Property
Now the callers don't get to see the private DelMonth, and all they can do with DeliveryMonth is call the Get accessor, which doesn't let them tamper with the encapsulated value.
It doesn't even throw an error.
That's worrying. You're allowing VBA to happily compile typos and otherwise illegal code. Specify Option Explicit at the top of every module. Always.

Reference to a non-shared member requires reference

I am updating some code from a vb6 application to VB.NET.
I have a problem that occurs when I try to open a form from the main form.
It calls this function:
Public Sub optDrawSafeFile_CheckedChanged(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles optDrawSafeFile.CheckedChanged
If eventSender.Checked Then
'--------------------------------------------------------------------------------
' JRL 11-03-06
' change the enables
UpdateGUI((False))
cboProject.SelectedIndex = frmMain.cboProjects.SelectedIndex
SelectJob()
End If
End Sub
And when it goes to execute this line:
cboProject.SelectedIndex = frmMain.cboProjects.SelectedIndex
It blows up and says this:
frmMain is declared like this:
How can I fix this error?
TL;DR
It is described in more detail in this video.
Short answer: change frmMain to My.Forms.frmMain.
cboProject.SelectedIndex = My.Forms.frmMain.cboProjects.SelectedIndex
Long answer:
In VB6, referencing a form by its name allowed you to access it both as a class and an instance of that class. The instance that you access in this manner is called the default instance. This is not possible in VB.NET. However, VB.NET includes a dynamically generated class, My.Forms, that provides functionality similar to that of default instances.
See http://msdn.microsoft.com/en-us/library/ms379610%28v=vs.80%29.aspx#vbmy_topic3 for more information about My.Forms and the "My" namespace.
A better and more object-oriented way to handle this, however, would be to pass the instance of the main form to the constructor of the frmAddMethod form and store it in an instance field.
So, within the class definition in frmAddMethod.vb:
Sub New(ByVal mainForm As frmMain)
_mainForm = mainForm
End Sub
Private _mainForm as frmMain
And when you create the frmAddMethod instance from frmMain, pass in "Me" to the constructor:
Dim addMethodForm as new frmAddMethod(Me)
"Me" is the instance of the class from which a non-shared class method was called.
This will allow you to use the _mainForm class field to access the instance of the main form from non-shared methods of frmAddMethod.
*Edited to recommend My.Forms instead of DefInstance per Plutonix's comment.
Nothing is "blowing up", your program is not crashing. Using a type name, like frmMain, where an object reference is expected is something that the VB.NET compiler allows. Specifically for the Form class, an appcompat hack for VB6 code. It is the debugger that doesn't think much of it. It merely gives you a diagnostic on your watch expression since it doesn't have the same appcompat hack as the compiler does. So doesn't know what to display.
You can use My.Forms instead to get the active form object reference. So make your watch expression:
My.Forms.frmMain.cboProjects.SelectedIndex
Only do this when you are single-stepping the code, it will still go wrong if you use Debug + Break All to break into the program. Setting a watch expression on Me.cboProject is otherwise the obvious workaround in this specific case.

Is it possible to declare a local/class instance member reference variable in VB.net to read/update same object referred by another variable

In C++, it is possible to do:
int x;
int& foo = x;
// foo is now a reference to x so this sets x to 56
foo = 56;
But is an equivalent possible in Visual Basic .net (VB.net)?
I am aware of the ByRef term in VB.net but I have only seen (and by extensive research) this in passing parameters by reference to functions/subroutines, so that the function modifies the same variable declared by the calling code.
But I haven't seen ByRef used to declare variables (as class members or local variables). I have tried to use ByRef to do such a declaration but I get compiler errors for each ordering of the declaration I try.
The reason why I want to do this is because I want a class instance to be able to refer to a variable declared in another class instance.
Specifically, I am working on some existing VB.net code where program logic is mixed up with UI logic in VB.net form classes. I want to separate the program logic out of the form code so that it can be more easily reused. To do this I need the program logic code to be able to 'talk' to the form code so I need access to its variables.
I am using Visual Studio 2010 Express with VB.net and would prefer to remain within this environment even though I am aware that full Visual Studio has extended capabilities.
Pointers exist but they're called object references
(Now other posters, please don't quibble with me about the actual differences here. I am talking about the high level task the OP wants to accomplish)
Obviously you can't do literally what you've said - that is, surgically manipulate one member. But if you have control of the code, you can do almost as well. And from what you describe is your problem, this method will be much better, as you can pass references to an object that has many members you wish to update, instead of having to pass many individual pointers.
First define a class:
Class MyScreenValues
' Properties will work... using public fields for brevity
Public TextBox1Value as String
Public SpinControl1Value as Integer
public CheckBox1Value as Boolean
End Class
Now the equivalent of the code you posted:
Dim x as new MyScreenValues 'int x;
dim foo as MyScreenValues = x 'int& foo = x;
'// c++: foo is now a reference to x so this sets x to 56
'// .net: foo is now a reference to x so this sets x.SpinControl1Value to 56
foo.SpinControl1Value = 56;
If what you're doing is trying to pass pointers to, say, every control on your form's value to a sub, like so:
Button1_Click(...
Dim MyObject as new BusinessObject
MyObject.DoSubmit(TextBox1.Text, SpinButton1.Value, CheckBox1.Checked)
You can use the method provided by superbDeveloper, and use the ByRef keyword on the definition of the DoSubmit Sub:
Public Sub DoSubmit(ByRef Name as String, ByRef Age as Integer, ByRef Employed as boolean)
... business logic...
However this just gives you a 2-layer separation. Look into MVP, MVC, etc - however consider submitting the entire view worth of data. It's a lot of research you you may settle with what you have now. For example your business logic will be firing events on the form as it changes the form values (well, actually, they won't fire until the sub exits, due to the way VB deals with byref properties [=temp variable], but it needs to be considered).
You can also map an object's properties to a Form's properties with other libraries. Check out VALUE INJECTOR on the web. It can take a webform/winform and maybe even a WPF form and map the control values to and from an object you have predefined. Excellent for complete separation.
Hi well not sure about VB.NET but I know how do it in C#, therefore I did the solution in C# and then i used a C# to VB.NET convertor to get the VB.NET code. maybe it might help you:
http://www.developerfusion.com/tools/convert/csharp-to-vb/
C# Code
protected void TestFoo()
{
string strFooA = string.Empty;
GetFoo(ref strFooA);
Response.Write(strFooA);
}
private void GetFoo(ref string strFooA)
{
strFooA = "FooA";
}
VB.NET converted
Protected Sub TestFoo()
Dim strFooA As String = String.Empty
GetFoo(strFooA)
Response.Write(strFooA)
End Sub
Private Sub GetFoo(ByRef strFooA As String)
strFooA = "FooA"
End Sub
Pointers don't exist in the .NET framework like they do in C/C++, so you won't be able to directly achieve what you want.
There are some possible solutions on this SO page
See #NathanW answer on there:
If you are using VB the only thing that is really close to a pointer is a IntPtr. If you have access to C# you can use unsafe C# code to do pointer work.
In Addition
If you want to be able to use references then you can achieve this by using a class:
Private Class RefClass
Public x As Integer
End Class
Then you can reference one from the other:
Dim bar As New RefClass
Dim foo As RefClass = bar
bar.x = 45
Debug.WriteLine(bar.x) 'outputs 45
Debug.WriteLine(foo.x) 'outputs 45
You may already know enough to get the job done from the info at http://www.dreamincode.net/forums/topic/135354-reference-types-value-types-byval-byref/ ; however, your remark "ByRef is not required in declaration of local variables (and possibly class variables)" left me wondering if there's some confusion here.
For starters, the C++ example above, int x; int& foo = x; confuses what is meant by a "reference". My C++ isn't very strong, but, semantically speaking, I believe this C++ reference operates more like an aliasing mechanism. In VB (and C#) a reference operates like an identification code that locates an instance of a class in memory. Neither VB or C# has anything like the C++ reference type.
By now, you probably already know you can pass in your Form as a ByVal parameter, and change its properties and fields without problem. (ByVal is the default in VB.NET, so you don't even need to use it - in VB6 ByRef was the default.) If you're happy enough, you can skip the rest. But, yeah, in the case of .NET Reference variables, assuming:
Dim objA as New MyClass()
Dim objB as MyClass = objA
Then objA and objB both reference the very same instance of MyClass. You can modify via objA and objB, and you can read back from either objA or objB, because they each affect the very same instance. You can pass either objA or objB into some subroutine Foo with parameter objC As Object passed ByVal (i.e. Sub Foo(ByVal objC As Object) ) and Foo can then modify that same instance too.
The ByRef of VB and the ref of C# indicate a modifiable parameter, which means some "identification code" reference is passed instead of a value. Yet this ByVal vs. ByRef thing is clear as mud because in .NET there is a distinction made between "Value" types and "Reference" types that confuses many on whether a ByRef or ref is needed or not.
Visual Basic and C# dichotomizes variables (and data types) into two species: the "Value" (or "Structure"), and the "Reference" (or "Class").
The "Value" type means an actual collection of bits that represents an Integer, or a Boolean, or even a bitmap, or some other kind of object. In old school parlance, this is the "image" of the instantiation of an object. It is the state space of the object. It is what makes an object essentially itself, independent of where in memory it may be.
The "Reference" type means a code (which might look like an integer or a pointer) that somehow indicates the data type of the object and where in memory it resides. The computer will interpret a "Reference" to obtain the actual image of the object (i.e. its "Value").
When a "Value" parameter is passed ByVal, that means a new object is created that is in the identical image of the original expression being passed, and it is upon this copy that the function or method operates. The original image of the "Value" cannot be affected.
When a "Value" parameter is passed ByRef, that means a new "Reference" variable is created, and that "Reference" variable will contain the information that will interpret back to the image of the original "Value". Now the original image of the "Value" can be changed.
When a "Reference" parameter is passed ByVal, its "identification code", which gets interpreted back to the actual image of the object, gets copied. It is upon this copy of the code that the function or subroutine or method operates. This copy still points to the actual image of the object. Which means that an object of a Reference variable that is passed by ByVal can still have its image (i.e. its "Value") changed. However, the code of the original "Reference" itself cannot be changed.
(Note that the String type is an odd duck: It will behave as if it were a "Value" parameter even though it is in fact a "Reference" type. Hence a String passed ByVal will not be affected in the same way any other class would. Actually, String is an example of an immutable type - which means that steps are taken to prevent changes to its "Value".)
When a "Reference" parameter is passed ByRef, one now has created a new "Reference" object that points to the original "Reference" object (that, in turn, points to the "Value" of some other object via its "identification code"). The use of ByRef on a "Reference" allows one to modify (or create anew) the "identification code" of the original "Reference" object being passed as a parameter. A function or subroutine or method that performs a swap operation will use ByRef on "Reference" parameters.

Why is it not necessary to indicate ByVal/ByRef anymore?

I just installed Visual Studio 2010 Service pack (proposed on Windows Update), and I can see a new feature on the "intellisense" that means when I write a Function or Sub in VB.NET it doesn't auto-complete parameters with ByRef or ByVal...
1) Is there anyway that I can configure this option back to how it was before?
2) If I don't specify ByX, which one is used by default? (it seems like it is always ByRef)
It seems that this post covers your question:
http://msmvps.com/blogs/carlosq/archive/2011/03/15/vs-2010-sp1-changing-quot-byval-quot-vb-net-code-editor-experience.aspx
So no, there is no way to get the old behaviour. From now on ByVal is the default (what it was before) and it won't get added automatically to the method parameters.
In my opinion this is a good decision since it's making VB.NET a bit more consistent with C# and avoids unnecessary "noises"(it's already verbose enough).
Old behaviour:
Private Sub test(ByVal test As String)
End Sub
New behaviour
Private Sub test(test As String)
End Sub
Tim covered what you asked directly, but something else to keep in mind is that any reference type variable, like a user defined class even if passed by value will allow you to make changes to that instances properties etc that stay. It won't however allow you to change the entire object. Which may be why it seemed to you to be defaulting to by reference
Public Sub (Something As WhateverClass)
Something = New WhateverClass 'will result in no changes when outside this method
Something.Property1 = "Test" 'will result in an updated property when outside this method
End Sub
From MSDN:
The value of a reference type is a pointer to the data elsewhere in memory.
This means that when you pass a reference type by value,
the procedure code has a pointer to the underlying element's data,
even though it cannot access the underlying element itself. For
example, if the element is an array variable, the procedure code does
not have access to the variable itself, but it can access the array
members.
Beware when transferring routines to VBA, where the default is ByRef (see, e.g., "The Default Method Of Passing Parameters" at the bottom of this page, by the great Chip Pearson).
That can be messy.