a byval-byref-optional-generic oddity

A

AMercer

Consider this sub:

Public Sub x(Of T)(Optional ByVal y As T = Nothing)
End Sub

It diagnoses with "Generic parameters used as optional parameter types must
be class constrained". It diagnoses for both option strict on and off.
Change ByVal to ByRef, and it does not diagnose. This looks like a compiler
bug to me.
 
T

Tom Shelton

Consider this sub:

Public Sub x(Of T)(Optional ByVal y As T = Nothing)
End Sub

It diagnoses with "Generic parameters used as optional parameter types must
be class constrained". It diagnoses for both option strict on and off.
Change ByVal to ByRef, and it does not diagnose. This looks like a compiler
bug to me.

Hmmm... It makes sense that it wouldn't allow you to set it to Nothing,
without constraining T:

Public Sub x(Of T As Class)(Optional ByVal y As T = Nothing)
End Sub

What I can't figure out is if it is right when you use ByRef rather then
ByVal. That seems wrong.
 
A

AMercer

Hmmm... It makes sense that it wouldn't allow you to set it to Nothing,
without constraining T:

That doesn't bother me - you can assign Nothing to a variable of any
reference type, and I think also of any value type, including structures.
That being true (but stylistically objectionable in some cases), it isn't a
stretch to use Nothing as the default value of an optional generic parameter.
What I can't figure out is if it is right when you use ByRef rather then
ByVal. That seems wrong.

Yes - I can't see what ByRef/ByVal has to do with it, and that is what
smells like a VB compiler bug.
 
T

Tom Shelton

That doesn't bother me - you can assign Nothing to a variable of any
reference type, and I think also of any value type, including structures.

Well... I suppose you can in VB. I'm thinking in C# :) To be honest
though, I would avoid optional parameters for the most part anyway.
They are really unecessary with function overloading...
That being true (but stylistically objectionable in some cases), it isn't a
stretch to use Nothing as the default value of an optional generic parameter.


Yes - I can't see what ByRef/ByVal has to do with it, and that is what
smells like a VB compiler bug.

That's the part that seems weird.
 
R

rowe_newsgroups

Consider this sub:

Public Sub x(Of T)(Optional ByVal y As T = Nothing)
End Sub

It diagnoses with "Generic parameters used as optional parameter types must
be class constrained". It diagnoses for both option strict on and off.
Change ByVal to ByRef, and it does not diagnose. This looks like a compiler
bug to me.

I also haven't decided whether I agree with no error on the ByRef.
You're actually passing the pointer to the object, so you could pass
'nothing' and still be right?

Either way, I would pull that Optional out right away and replace it
with overloading.

Thanks,

Seth Rowe [MVP]
 
H

Herfried K. Wagner [MVP]

AMercer said:
Consider this sub:

Public Sub x(Of T)(Optional ByVal y As T = Nothing)
End Sub

It diagnoses with "Generic parameters used as optional parameter types
must
be class constrained". It diagnoses for both option strict on and off.
Change ByVal to ByRef, and it does not diagnose. This looks like a
compiler
bug to me.


I think that the compiler is correct. However, I think that this limitation
could be removed even for 'ByVal' because the default value of a value type
is a defined value (everything set to zero). Note that you can use
'Nothing' to reset a variable of a custom structure:

\\\
Dim v As MyStructure
v.Bla = ...
v = Nothing ' Reset the structure.
///

When passing the optional parameter 'ByRef', a reference to the value is
passed instead of the value itself.
 
A

AMercer

I think that the compiler is correct.

So with ByVal, you get a compiler diagnostic, and with ByRef you don't, and
you say that the compiler is correct. I just don't get it. In the setting
of the original question, ByVal/ByRef should not govern whether or not you
get a diagnostic. My assertion of a compiler bug is about the different
treatment of ByVal vs ByRef. They should both diagnose, or they should both
not diagnose. Can you explain why they should be treated differently?
 
R

rowe_newsgroups

So with ByVal, you get a compiler diagnostic, and with ByRef you don't, and
you say that the compiler is correct. I just don't get it. In the setting
of the original question, ByVal/ByRef should not govern whether or not you
get a diagnostic. My assertion of a compiler bug is about the different
treatment of ByVal vs ByRef. They should both diagnose, or they should both
not diagnose. Can you explain why they should be treated differently?

I think he (and I) did:

"When passing the optional parameter 'ByRef', a reference to the value
is
passed instead of the value itself."

and

"I also haven't decided whether I agree with no error on the ByRef.
You're actually passing the pointer to the object, so you could pass
'nothing' and still be right?"

Changing it to ByRef makes it so that any possible type could be
passed as nothing, as you are not passing the actually type (which
might not be nullable) but just passing the reference, which can be
null. Since no constraint is needed, the compiler will not give you an
error/warning.

Thanks,

Seth Rowe [MVP]
 
A

Armin Zingler

AMercer said:
So with ByVal, you get a compiler diagnostic, and with ByRef you
don't, and you say that the compiler is correct. I just don't get
it. In the setting of the original question, ByVal/ByRef should not
govern whether or not you get a diagnostic. My assertion of a
compiler bug is about the different treatment of ByVal vs ByRef.
They should both diagnose, or they should both not diagnose. Can
you explain why they should be treated differently?

With ByVal, the value is on the stack. Setting it to the Default value,
which is Nothing, is generally handled differently with value types and
reference types. Using ByVal, the compiler doesn't know which code to
generate in order to set the default value becaues it can either be a
reference or a value. Using ByRef(erence), this problem does not occur
because it is always a reference.

IMO only. Maybe I'm wrong.


Armin
 
A

AMercer

Either way, I would pull that Optional out right away and replace it
with overloading.

Tom Shelton also advises using overloads instead of optional parameters. VB
and C++ allow optional parameters, C# does not, and I understand a blanket
objection to optional parameters based on this fact. Setting that aside, is
there any other objection? I think both optional parameters and overloads
are good programming constructs. If creating an overload will result in two
subs with nearly identical code, I will switch to an optional parameter
solution.

So, are there any non-C# objections? Would you welcome an enhancement to C#
that allowed optional arguments?
 
H

Herfried K. Wagner [MVP]

AMercer said:
So with ByVal, you get a compiler diagnostic, and with ByRef you don't,
and
you say that the compiler is correct. I just don't get it. In the
setting
of the original question, ByVal/ByRef should not govern whether or not you
get a diagnostic. My assertion of a compiler bug is about the different
treatment of ByVal vs ByRef. They should both diagnose, or they should
both
not diagnose. Can you explain why they should be treated differently?

As I already said, I do not really see any reason for the existance of the
compiler warning at all. The only reason I can think of is that the feature
is not yet implemented by the compiler and thus results in a warning ;-).
 
A

AMercer

"I also haven't decided whether I agree with no error on the ByRef.
You're actually passing the pointer to the object, so you could pass
'nothing' and still be right?"

Changing it to ByRef makes it so that any possible type could be
passed as nothing, as you are not passing the actually type (which
might not be nullable) but just passing the reference, which can be
null. Since no constraint is needed, the compiler will not give you an
error/warning.

I still dont get it - I plan to omit the parameter in the call, I do not
plan "just passing the reference, which can be null". Remember the original
question involved generics, optional parameters, and ByVal vs ByRef. For the
two subs:

Public Sub xval(Of T)(Optional ByVal y As T = Nothing)
End Sub

Public Sub xref(Of T)(Optional ByRef y As T = Nothing)
End Sub

The calls in question omitting the optional parameters are

xval()
xref()

In both cases, T cannot be resolved to a type. In my view, the compiler bug
is that the the ByRef sub should diagnose but does not. The ByVal case
diagnoses correctly.
 
T

Tom Shelton

Tom Shelton also advises using overloads instead of optional parameters. VB
and C++ allow optional parameters, C# does not, and I understand a blanket
objection to optional parameters based on this fact. Setting that aside, is
there any other objection? I think both optional parameters and overloads
are good programming constructs. If creating an overload will result in two
subs with nearly identical code, I will switch to an optional parameter
solution.

So, are there any non-C# objections? Would you welcome an enhancement to C#
that allowed optional arguments?

The main reason I object to optional parameters has to do with the fact
that they are compiled into the il, and essentially become part of the
interface.

And no, I wouldn't welcome them as a C# addition. You can accomplish
the same thing using overloads:

Public Sub DoSomething ()
DoSomething (0)
End Sub

Public Sub DoSomething (ByVal i As Integer)
Console.WriteLine (i)
End Sub

See, you just pass the "optional" parameters into the other
implementation.
 
H

Herfried K. Wagner [MVP]

AMercer said:
I still dont get it - I plan to omit the parameter in the call, I do not
plan "just passing the reference, which can be null". Remember the
original
question involved generics, optional parameters, and ByVal vs ByRef. For
the
two subs:

Public Sub xval(Of T)(Optional ByVal y As T = Nothing)
End Sub

Public Sub xref(Of T)(Optional ByRef y As T = Nothing)
End Sub

The calls in question omitting the optional parameters are

xval()
xref()

In both cases, T cannot be resolved to a type. In my view, the compiler
bug
is that the the ByRef sub should diagnose but does not. The ByVal case
diagnoses correctly.

Why should the compiler raise the warning for 'ByRef' if everything seems to
work.

It's pretty clear that the compiler cannot infer the type of the parameter
in the two calls above. However, automatic type inference does not work in
all scenarios of generic parameters, not limited to cases involving optional
parameters.

You should still be able to call 'xval(Of Integer)()', 'xval(Of Form)()',
'xref(Of Integer)()', and 'xref(Of Form)()' respectively.
 
H

Herfried K. Wagner [MVP]

Tom Shelton said:
The main reason I object to optional parameters has to do with the fact
that they are compiled into the il, and essentially become part of the
interface.

Well, that's the main reason I /use/ optional parameters instead of
overloads.

Have you ever wondered which value for 'FileShare' is used when opening a
'FileStream' using an overload which does not have an according parameter?
Optional parameters enforce a stricter contract than overloads which have
crucial side-effects only stated in the documentation.
 
T

Tom Shelton

Well, that's the main reason I /use/ optional parameters instead of
overloads.

Have you ever wondered which value for 'FileShare' is used when opening a
'FileStream' using an overload which does not have an according parameter?
Optional parameters enforce a stricter contract than overloads which have
crucial side-effects only stated in the documentation.

Funny how two people can view the same problem differently ;) Another
downside, If you intend your class to be used cross language, then you
may introduce issues - for instance you make the class more difficult to
use from C#, since you are required to pass all arguments :)
 
A

AMercer

Herfried K. Wagner said:
Why should the compiler raise the warning for 'ByRef' if everything seems to
work.

It's pretty clear that the compiler cannot infer the type of the parameter
in the two calls above. However, automatic type inference does not work in
all scenarios of generic parameters, not limited to cases involving optional
parameters.

You should still be able to call 'xval(Of Integer)()', 'xval(Of Form)()',
'xref(Of Integer)()', and 'xref(Of Form)()' respectively.

Ok, I agree, and I change my mind completely. Now I say VB handles the
ByRef case correctly and incorrectly diagnoses the ByVal case. Can you tell
me why I'm wrong now? (I'm not being frivolous - I'm half way there to
understanding this.)

Also, from the description of "Generic parameters used as optional parameter
types must be class constrained" comes:

"When you use a type parameter for an optional parameter, you must guarantee
that it is of a reference type to avoid the possibility of a value type with
no valid default value. This means you must constrain the type parameter
either with the Class keyword or with the name of a specific class."

Do you know of any value type where Nothing is not a valid assignment? For
example,
Dim x as SomeValueType ' if this compiles...
x = Nothing ' ...then so does this
In all cases of value types that I have looked at, x=Nothing is a valid
assignment, hence ought to be a valid default for an optional parameter.
Even an enum with no zero value still allows an assignment of Nothing.
 
H

Herfried K. Wagner [MVP]

AMercer said:
Ok, I agree, and I change my mind completely. Now I say VB handles the
ByRef case correctly and incorrectly diagnoses the ByVal case. Can you
tell
me why I'm wrong now? (I'm not being frivolous - I'm half way there to
understanding this.)

Well, it seems that you have adopted my opinion (I didn't take a look at the
language specs to back it up yet).
Also, from the description of "Generic parameters used as optional
parameter
types must be class constrained" comes:

"When you use a type parameter for an optional parameter, you must
guarantee
that it is of a reference type to avoid the possibility of a value type
with
no valid default value. This means you must constrain the type parameter
either with the Class keyword or with the name of a specific class."

Do you know of any value type where Nothing is not a valid assignment?

No. That's why I am surprised by the compiler warning.
 
A

AMercer

You beat me to the punch - I was about to post a 'nevermind' because it
looked like I'd adopted your opinion from an earlier post. Thanks.
 
R

rowe_newsgroups

:






Ok, I agree, and I change my mind completely. Now I say VB handles the
ByRef case correctly and incorrectly diagnoses the ByVal case. Can you tell
me why I'm wrong now? (I'm not being frivolous - I'm half way there to
understanding this.)

Also, from the description of "Generic parameters used as optional parameter
types must be class constrained" comes:

"When you use a type parameter for an optional parameter, you must guarantee
that it is of a reference type to avoid the possibility of a value type with
no valid default value. This means you must constrain the type parameter
either with the Class keyword or with the name of a specific class."

Do you know of any value type where Nothing is not a valid assignment? For
example,
Dim x as SomeValueType ' if this compiles...
x = Nothing ' ...then so does this
In all cases of value types that I have looked at, x=Nothing is a valid
assignment, hence ought to be a valid default for an optional parameter.
Even an enum with no zero value still allows an assignment of Nothing.

I think it might be helpful for you to take a look at how the compiler
handles setting a value type to 'Nothing'

Take this code for example:

/////////////////
Sub Main()
Dim i As Integer = 5
Console.WriteLine(i)
i = Nothing
Console.WriteLine(i)
Console.Read()
End Sub
/////////////////

If you view the compiled code as VB with reflector it's back to:

////////////////
<STAThread> _
Public Shared Sub Main()
Dim i As Integer = 5
Console.WriteLine(i)
i = 0
Console.WriteLine(i)
Console.Read
End Sub
////////////////

It doesn't actually set the value to 'Nothing' it just knows to reset
the value type back to it's default. This behavior seems to me as just
a throw back to the classic days, a VB specific change (you can't set
value types to null in C#) that was allowed just for upgrading apps
and allowing easy upgrading for classic VB programmers. As to why it
doesn't do this with the generics I don't know. Perhaps with the 2.0
compiler and the guidelines for generics the VB team decided to not to
do something VB specific, especially since generics were not available
in classic VB.

Thanks,

Seth Rowe [MVP]
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top