Description
Scenario
Unlike C#, VB doesn't use a keyword at the call-site to denote whether an argument is being passed ByRef
or not. That's pretty reasonable given that ByRef was the default in VB6 and even C# drops the requirement to use ref
when inter-operating with COM.
All that said, despite not being CLS-compliant from time to time one sees an API which includes overloads which differ only by the refness of one or more arguments. I'm not sure why they do this; perhaps it's a versioning thing--an initial version is published with a ByVal argument and later a ByRef one is added for performance.
This puts VB users in a bad spot if they encounter one of these bad-actors because VB can't distinguish these overloads at the call-site. I've run into it myself when using the Managed DirectX library and have seen at least one customer report about it.
Note: This proposal does NOT include allowing Visual Basic to define such overloads.
Proposal
Given two methods M
and N
of equal specificity, and a pair of parameters Mj
and Nj
that match argument Aj
,
- If
Aj
is classified as a variable or property access andMj
is a reference (ByRef
) parameter andNj
is not,Mj
is more specific thanNj
. - If
Aj
is classified as a value andMj
is a value (ByVal
) parameter andNj
is not,Mj
is more specific thanNj
. - If at least one
Mj
is more specificNj
and noNj
is more specific thanMj
,M
is more specific thanN
.
Note: This does not take into account whether the variable or property being accessed is read-only.
More plainly, prefer ByRef
parameters when passing "r-values" (things that can be assigned to) and ByVal
parameters when passing l-values
(things which aren't storage locations).
This means given these overloads:
Sub M(ByVal p As Integer)
Sub M(ByRef p As Integer)
A user can force the compiler to pick the first overload by forcing the argument to be classified as a value (e.g. parenthesizing the expression).
A user can force the compiler to pick the second overload by manually copying the value into a local variable (which is what the compiler would have done anyway).
And without user intervention the compiler will pick the overload whose argument passing convention is most appropriate for the class of expression being passed.
Alternatives
Add a ByRef
modifier at the call-site. This has been requested before but was rejected due to concerns that some code would use it and some wouldn't and without strict enforcement (breaking change) it would cause more confusion than good as readers would erroneously assume code without the modifier wasn't passing ByRef
.