Reputation: 295
I have some procedures that take Control
object references as a parameter.
I have a bunch of Control
s 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?
Upvotes: 0
Views: 217
Reputation: 54417
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.
Upvotes: 1
Reputation: 117064
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
Upvotes: 3