.NETGURU
enumerations as parameters, VB, and the value 0
Messages   Related Types
This message was discovered on microsoft.public.dotnet.framework.clr.
Responses highlighted in red are from those people who are likely to be able to contribute good, authoratitive information to this discussion. They include Microsoft employees, MVP's and others who IMHO contribute well to these kinds of discussions.
Post a new message to this list...

Plausible Indirection
I'm having some trouble with the value 0 and how VB is resolving its
type when it is used as a parameter (to a constructor in this case).
The same problem does not reproduce in C#; the correct constructor is
always called.

Sorry for the length, but the best description is simplified code:

Module Module1

Enum Bar
Bar0 = 0
Bar1 = 2
Bar2 = 4
End Enum

Structure Foo
Dim field1 As Int32
Dim state1 As Bar
Dim state2 As Bar

Public Sub New(ByVal f1 As Int32, ByVal b2 As Bar)
field1 = f1
state1 = Bar.Bar0
state2 = b2
End Sub

Public Sub New(ByVal s1 As Bar, ByVal s2 As Bar)
field1 = 0
state1 = s1
state2 = s2
If state1 >= state2 Then
Throw New ArgumentException
End If
End Sub

End Structure
Sub Main()
Try
Dim dt As New Foo(1, Bar.Bar0)
System.Console.WriteLine("Problem did not reproduce. value of 1 was
accepted.")

dt = New Foo(2, Bar.Bar0)
System.Console.WriteLine("Problem did not reproduce. value of 2 was
accepted.")

dt = New Foo(0, Bar.Bar0)
System.Console.WriteLine("Problem did not reproduce. value of 0 was
accepted.")

Catch ex As System.Exception
System.Console.WriteLine("Problem reproduced. value of 0 caused this
System.Exception." + vbCrLf)

System.Console.WriteLine("Error: " + ex.ToString)
End Try

End Sub

End Module

The values 1 and 2 work. On the last constructor call, VB runtime
resolves this to the enum-enum constuctor rather than the the int-enum
constructor. Is this expected? Why should any language use type
resolution that is value dependent (except for range problems, which
this isn't).

Any takers?

Thanks,
Chris
Reply to this message...
 
    
Jay B. Harlow [MVP - Outlook] (VIP)
Chris,
I will see what I can find out as to Why.

I know resolving to the Enum overload is how it works, I just don't remember
the why.

More later
Jay

"Plausible Indirection" <Click here to reveal e-mail address> wrote in message
news:Click here to reveal e-mail address...
[Original message clipped]

Reply to this message...
 
    
JD
Its not the runtime but the compiler. Look at the IL output from the VB.NET
compiler versus C# compiler. C# does it correct but VB.NET does not. Bug?

"Plausible Indirection" <Click here to reveal e-mail address> wrote in message
news:Click here to reveal e-mail address...
[Original message clipped]

Reply to this message...
 
    
Jay B. Harlow [MVP - Outlook] (VIP)
Chris,
[Original message clipped]

Resolving to the enum-enum constructor over the int-enum constructor is the
expected behavior in VB.NET 2002 & 2003. As the literal 0 (remember that it
is a literal 0, not an integer 0!) widens to enumerated types, it also
widens to an Integer (short, long). The Enumerated type also widens to an
Integer.

The Enumerated type is more specific then an Integer type, hence the
Enum-Enum constructor is going to be selected.

I have not tried this in VB.NET 2005 Beta 1 to see if the same rules apply
or not... (VS.NET 2005 will be available sometime in 2005).

Hope this helps
Jay

"Plausible Indirection" <Click here to reveal e-mail address> wrote in message
news:Click here to reveal e-mail address...
[Original message clipped]

Reply to this message...
 
    
JD
Looking at the VB compiler IL output and the difference between the call
with 2 versus the call with 0.

-- 2
IL_0013: ldloca.s _Vb_t_record_0
IL_0015: ldc.i4.2
IL_0016: ldc.i4.0
IL_0017: call instance void Module1/Foo::.ctor(int32,valuetype
Module1/Bar)

-- 0
IL_0028: ldloca.s _Vb_t_record_0
IL_002a: ldc.i4.0
IL_002b: ldc.i4.0
IL_002c: call instance void Module1/Foo::.ctor(valuetype
Module1/Bar, valuetype Module1/Bar)

Why does the compiler choose to widen 0 to enumerated type but chooses to
widen 2 to the integer type?

JD

"Jay B. Harlow [MVP - Outlook]" <Click here to reveal e-mail address> wrote in message
news:Click here to reveal e-mail address...
[Original message clipped]

Reply to this message...
 
    
Jay B. Harlow [MVP - Outlook] (VIP)
JD,
[Original message clipped]

As I stated the enumerated type is more specific then an Integer!

The compiler looks for the most specific overload and selects it. Seeing as
the enum is more specific it gets picked!

Enum Bar is an "Integer", by virtue of all Enums are implemented in terms on
an ordinal value type (Byte, Short, Integer, Long).

The Literal 0 could be a Byte, Short, Integer, Long or Enum Bar. The
compiler looks for the most specific type that the literal 0 fits into of
the overloaded methods. Enum Bar is the most specific, hence it gets used.

Take a step back and consider only Object & Integer overloads. Integer is
more specific then Object so Integer is selected.

Same with Integer & Enum, Enum is more specific then Integer, so Enum is
selected.

Hope this helps
Jay

"JD" <Click here to reveal e-mail address> wrote in message
news:0jKTc.260852$%_6.180608@attbi_s01...
[Original message clipped]

Reply to this message...
 
    
cody
> As I stated the enumerated type is more specific then an Integer!

Why is it more specific? They both inherit from valuetype.
If I were compiler designer I wouldn't allow 0 to implicitly cast to an enum
because not all enums have a valid 0 value! And even if I would allow this,
I wouldn't allow a call for Foo(0) if there is an int and enum overload.
I would raise a compiler error stating that 0 must be explicitly casted to
the specific enum type, otherwise we have an ambiguity.

--
cody

Freeware Tools, Games and Humour
http://www.deutronium.de.vu || http://www.deutronium.tk

Reply to this message...
 
    
Jon Skeet [C# MVP] (VIP)
cody <Click here to reveal e-mail address> wrote:
[Original message clipped]

It's more applicable because there is a widening conversion from the
enum to Integer, but no widening conversion from Integer to enum.

--
Jon Skeet - <Click here to reveal e-mail address>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Reply to this message...
 
    
cody
[Original message clipped]

what do you mean with widening conversion? int and enum are the same size.
But now the question arises what if you have enum Test: long{A,B,C} ?

--
cody

Freeware Tools, Games and Humour
http://www.deutronium.de.vu || http://www.deutronium.tk
"Jon Skeet [C# MVP]" <Click here to reveal e-mail address> schrieb im Newsbeitrag
news:Click here to reveal e-mail address...
[Original message clipped]

Reply to this message...
 
    
Jon Skeet [C# MVP] (VIP)
cody <Click here to reveal e-mail address> wrote:
[Original message clipped]

I mean exactly what the VB.NET specification says. (See section 8.8 of
the spec in MSDN.)

> int and enum are the same size.

Irrelevant.

> But now the question arises what if you have enum Test: long{A,B,C} ?

There's still a widening conversion from 0 to the enum. There is also a
widening conversion from the enum to long, as stated in the language
specification as one of the list of widening conversions:

<quote>
Conversions from any enumerated type to its underlying type, or to any
type that its underlying type has a widening conversion to.
</quote>

--
Jon Skeet - <Click here to reveal e-mail address>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Reply to this message...
 
    
Jay B. Harlow [MVP - Outlook] (VIP)
Code,
A widening conversion is one where there is no loss of data. Integer allows
all values from Int32.MinValue to In32.MaxValue, while Enum only allows (not
enforced) the values that are defined on it (a sub set of Integer).

A narrowing conversion is one where there may be a loss of data.

A widening conversion is an implicit cast operation, while a narrowing
conversion is an explicit cast operation.

As Jon pointed out literal 0 is special in that an implicit cast is allowed,
while the literal 2 an explicit cast is required. Mostly because we know
that 0 is valid for the Enum (as it is its "default" value) while 2 may or
may not have been defined in the Enum.

Hope this helps
Jay

"cody" <Click here to reveal e-mail address> wrote in message
news:%Click here to reveal e-mail address...
[Original message clipped]

Reply to this message...
 
    
Plausible Indirection
I've been busy for a few days and I came back to a lot more discussion
on this than I expected at first. Thanks to all!

Ok, there seems to be some sloppiness in the language spec.
Basically, it does not enforce the restriction of binding of a value
to an enumeration type to be one of the values defined for that type.
Because of this lack of enforcement, it creates an implicit cast from
0 to enum that would otherwise not always be possible. It is quite
possible to define an enumeration that does not contain a valid value
for 0.

On top of that, I tried

dt = New Foo(CInt(0), Bar.Bar0)

I would think that the conversion function would remove any
possibility that the 0 could be interpreted as an enum, but the
behavior remains the same. This just doesn't make sense to me.

I also tried

Enum Bar
Bar0 = -1
Bar1 = -2
Bar2 = -4
End Enum

and again there was no behavior change. This really seems like a
weakness in the language because, it would be a whole lot simpler, and
IMHO, better, to define one rule for interpreting numeric literals as
a type rather than multiple rules based on what the value happens to
be. Ok, I'd allow promotion along the integer types, but still, in
this case, the literal 0 is no more a valid value for the enumeration
than the literal 2.

I guess you could argue that the language definition itself _makes_ 0
a more valid value than 2, but that is just a hair-puller for me.

-Chris

"Jay B. Harlow [MVP - Outlook]" <Click here to reveal e-mail address> wrote in message news:<OwpU#Click here to reveal e-mail address>...
[Original message clipped]

Reply to this message...
 
    
Jay B. Harlow [MVP - Outlook] (VIP)
Chris,
[Original message clipped]

:-|

As that is how it is currently defined to work! I stated I have not tried
VS.NET 2005 (aka Whidbey, due out in 2005) to see if things are improved or
not. I really hope they will be!

[Original message clipped]


Dim zero As Integer = 0

dt = New Foo(zero, Bar.Bar0)

As that is the only way I know of to get VS.NET 2002 & VS.NET 2003 to work.
Yes MS knows there is a problem! As I told them a year ago when I came
across this quirk and remembered them when you asked your original question.

[Original message clipped]

Remember that

Dim myBar As Bar

Will have the value of literal 0 per the CTS. Also remember that the CLR
does not validate values within an Enum, if you need validation of the
values you can use Enum.IsDefined.

If Not [Enum].IsDefined(GetType(Bar), myBar) Then
Throw New ArgumentOutOfRangeException("myBar", myBar, "Invalid Enum
Bar value!")
End If

Hope this helps
Jay

"Plausible Indirection" <Click here to reveal e-mail address> wrote in message
news:Click here to reveal e-mail address...
[Original message clipped]

Reply to this message...
 
    
Jon Skeet [C# MVP] (VIP)
Jay B. Harlow [MVP - Outlook] <Click here to reveal e-mail address> wrote:
> A widening conversion is one where there is no loss of data.

That's not quite true - at least not by the VB.NET specification. For
instance, there is a widening conversion from Integer to Single,
despite the fact that not all System.Int32 values are exactly
representable as System.Single values.

The VB.NET specification states:
<quote>
Widening conversions never overflow but may entail a loss of precision.
</quote>

--
Jon Skeet - <Click here to reveal e-mail address>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Reply to this message...
 
    
Jay B. Harlow [MVP - Outlook] (VIP)
Jon,
That's true, I simplified my statement.

Jay

"Jon Skeet [C# MVP]" <Click here to reveal e-mail address> wrote in message
news:Click here to reveal e-mail address...
[Original message clipped]

Reply to this message...
 
    
JD
Maybe I'm confused. The two calls below. 2 is not a literal but 0 is?

dt = New Foo(2, Bar.Bar0)

dt = New Foo(0, Bar.Bar0)

"Jay B. Harlow [MVP - Outlook]" <Click here to reveal e-mail address> wrote in message
news:Click here to reveal e-mail address...
[Original message clipped]

Reply to this message...
 
    
Jay B. Harlow [MVP - Outlook] (VIP)
JD,
Me bad. I miss read your question.

You are correct 2 is also a literal.

The literal 0 is treated special in that it will bind to the Enum. It binds
to the enum as 0 is the "default" value for the Enum. (It is the value that
is assigned when you do not initialize the Enum, or you assign Nothing to
the Enum).

The literal 2 does not follow the same rule as a literal 0.

Because literal 0 is used as the "default" value for an Enum, I normally
define a None value on my enums...

Enum Bar
None = 0
Bar0 = 1
Bar1 = 2
Bar2 = 4
End Enum

Hope this helps
Jay

"JD" <Click here to reveal e-mail address> wrote in message
news:siMTc.129168$8_6.17123@attbi_s04...
[Original message clipped]

Reply to this message...
 
    
JD
Aaah...default value for Value types. Thanks Jay and John.

"Jay B. Harlow [MVP - Outlook]" <Click here to reveal e-mail address> wrote in message
news:Click here to reveal e-mail address...
[Original message clipped]

Reply to this message...
 
    
Plausible Indirection
I thought the common type system of .Net was supposed to allow you to
expect the same behavior of the types across the languages, but that
isn't the case here. VB does one thing and C# another.

-Chris

"JD" <Click here to reveal e-mail address> wrote in message news:<Click here to reveal e-mail address>...
[Original message clipped]

Reply to this message...
 
    
cody
This has nothing to do with the CTS but only with the language used.

--
cody

Freeware Tools, Games and Humour
http://www.deutronium.de.vu || http://www.deutronium.tk
"Plausible Indirection" <Click here to reveal e-mail address> schrieb im Newsbeitrag
news:Click here to reveal e-mail address...
[Original message clipped]

Reply to this message...
 
    
Jon Skeet [C# MVP] (VIP)
Plausible Indirection <Click here to reveal e-mail address> wrote:
[Original message clipped]

In what way, exactly? There may be something, but I'm not seeing it
here. There's an implicit conversion between 0 and enums in C# too, and
the default value for an enum type is 0. Or are you talking about
overload resolution?

--
Jon Skeet - <Click here to reveal e-mail address>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Reply to this message...
 
    
Plausible Indirection
Well, overload resolution is determined by the parameter types, right?
You'll have a hard time convincing me that a 0 should not be of the
same type as 0.

The same program translated to C# does not give an error; or rather

dt = new Foo(0, Bar.Bar0);

resolves to

public Foo(Int32 f1, Bar b2)

So, a literal 0 in C# becomes an integer and a literal 0 in VB becomes
some kind of generic enumeration. I say generic enumeration because
if I add an enum Sna that is basically the same as Bar and

Public Sub New(ByVal s1 As Sna, ByVal s2 As Bar)

....

dt = New Foo(CInt(0), Bar.Bar0)

I get a compiler error that

error BC30521: Overload resolution failed because no accessible 'New'
is most specific for these arguments:
'Public Sub New(s1 As Sna, s2 As Bar)': Not most specific.
'Public Sub New(s1 As Bar, s2 As Bar)': Not most specific.

In contrast, the same thing in C# works; or rather, since 0 defaults
to an integer, the overload to use is never in question.

It seems that the creators of VB have lumped enumerations in with
integers, just a more specific variety, most likely because the
underlying implementation has the storage characteristic of an
integer. In OO programming, I prefer that the underlying storage or
implementation is entirely unknown on the surface. Playing with some
code,

Bar test;
test = Bar.Bar0 + Sna.Sna0;

gives a compile error; whereas,

Dim test As Bar
test = Bar.Bar0 + Sna.Sna0

assigns the value Bar1 to test.

It seems sloppy to me that two enumerations that are entirely
different, say Apples and Oranges, can be added or compared. For more
language differences between enum types compare

Boolean test;
test = (Bar.Bar0 == Sna.Sna0);

which gives a compiler error, with

Dim test As Boolean
test = (Bar.Bar0 = Sna.Sna0)

which assigns the value True to test.

That's about all I have to say on the subject. I concede that that's
the way it is and when some customer runs into this problem while
trying to use my type, I'll just have to tell them to assign the value
0 to a variable before using it in a constructor.

-Chris

Jon Skeet [C# MVP] <Click here to reveal e-mail address> wrote in message news:<Click here to reveal e-mail address>...
[Original message clipped]

Reply to this message...
 
    
Jon Skeet [C# MVP] (VIP)
Plausible Indirection <Click here to reveal e-mail address> wrote:
[Original message clipped]

<snip>

[Original message clipped]

It does, actually. For instance:

using System;

enum TestEnum
{
Foo=1
}

public class Test
{
static void Main()
{
DoSomething(0);
}

static void DoSomething(TestEnum x)
{
}
}

compiles fine, so clearly DoSomething(TestEnum x) is an appropriate
method to call - it's just that C#'s overload resolution is defined
slightly differently. I don't think that's either particualrly
surprising or a problem.

[Original message clipped]

Only when you don't have Option Strict On. There are any number of
things which are pretty horrible when you don't have Option Strict On -
this is far from the worst of them.

[Original message clipped]

Okay, that one still does compile with Option Strict On, unfortunately.

[Original message clipped]

You could try telling them to turn Option Strict On too, if they're
seeing problems like the first one.

--
Jon Skeet - <Click here to reveal e-mail address>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Reply to this message...
 
    
JD
Oops pulled the trigger too quick.

[Original message clipped]


Understood. I don't have a problem with the call with literal 0 finding the
enum overload. Its the call with literal 2 not finding the enum overload.
Doesn't the call with the literal 2 fall under the same rules as stated
above call with 0?

"JD" <Click here to reveal e-mail address> wrote in message
news:siMTc.129168$8_6.17123@attbi_s04...
[Original message clipped]

Reply to this message...
 
    
Jon Skeet [C# MVP] (VIP)
JD <Click here to reveal e-mail address> wrote:
[Original message clipped]

No - there's no implicit conversion between 2 and an enumeration type,
whereas there *is* an implicit conversion between 0 and any enumeration
type.

For instance:

Dim x as HttpStatusCode
x = 0 ' Valid
x = 1 ' Invalid

--
Jon Skeet - <Click here to reveal e-mail address>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Reply to this message...
 
 
System.ArgumentException
System.ArgumentOutOfRangeException
System.Console
System.Enum
System.Exception
System.Int32
System.Net.HttpStatusCode
System.Single




ExamGuru IT Solutions - .Net Guru is owned and operated by ExamGuru, Inc., the man behind .Net Guru. If you're in the market for bespoke software or software consultancy, why not get him and his highly trained team to help? - www.examguru.net/ITCertification
Ad


Need Dot Net Interview Questions?
Ask ExamGuru, Inc. for advice and help on Passing .Net Interviews
.Net Projects
Best-of-breed application framework for .NET projects, developed by ExamGuru, Inc. and ExamGuru IT
Free .net Help
Commission ExamGuru, Inc. and his team for your next bespoke software project
FogBUGZ
The only bug tracking system carefully crafted with one goal in mind: helping teams create great software.
Awesome Tools
If you don't know about these, you're missing out... IT Certification Questions
IT Interview Questions
Free Oracle 10g Training
MCSE Boortcamp
Cisco Study Guides
Cheap Study Guides
Exact Questions
Dot Net Interview Questions
Oracle OCP
Cheap Travel
Designer Perfumes - Wholesale Prices
Free Programming Tutorials
 
ExamGuru IT Solutions - .Net Guru is owned and operated by ExamGuru, Inc., the man behind .Net Guru. If you're in the market for bespoke software or software consultancy, why not get him and his highly trained team to help? - www.examguru.net/ITCertification
 Copyright © ExamGuru, Inc. 2001-2006
Contact Us - Terms of Use - Privacy Policy - www.dot-net-guru.com - www.examguru.net - www.oraclesource.net - www.itinterviews.net - www.examguru.net/ITCertification