Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / CLR / August 2004

Tip: Looking for answers? Try searching our database.

enumerations as parameters, VB, and the value 0

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Plausible Indirection - 10 Aug 2004 23:18 GMT
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
Jay B. Harlow [MVP - Outlook] - 11 Aug 2004 16:21 GMT
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

> 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).
[quoted text clipped - 67 lines]
> Thanks,
> Chris
JD - 11 Aug 2004 19:32 GMT
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?

> 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).
[quoted text clipped - 67 lines]
> Thanks,
> Chris
Jay B. Harlow [MVP - Outlook] - 15 Aug 2004 06:17 GMT
Chris,
> 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).

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

> 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).
[quoted text clipped - 67 lines]
> Thanks,
> Chris
JD - 15 Aug 2004 15:20 GMT
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

> Chris,
> > The values 1 and 2 work.  On the last constructor call, VB runtime
[quoted text clipped - 89 lines]
> > Thanks,
> > Chris
Jay B. Harlow [MVP - Outlook] - 15 Aug 2004 17:05 GMT
JD,
> Why does the compiler choose to widen 0 to enumerated type but chooses to
> widen 2 to the integer type?

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

> Looking at the VB compiler IL output and the difference between the call
> with 2 versus the call with 0.
[quoted text clipped - 113 lines]
> > > Thanks,
> > > Chris
JD - 15 Aug 2004 17:36 GMT
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)

> JD,
> > Why does the compiler choose to widen 0 to enumerated type but chooses to
[quoted text clipped - 140 lines]
> > > > Thanks,
> > > > Chris
JD - 15 Aug 2004 17:46 GMT
Oops pulled the trigger too quick.

> > 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.

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?

> Maybe I'm confused. The two calls below. 2 is not a literal but 0 is?
>
[quoted text clipped - 159 lines]
> > > > > Thanks,
> > > > > Chris
Jon Skeet [C# MVP] - 16 Aug 2004 09:04 GMT
> Oops pulled the trigger too quick.
>
[quoted text clipped - 8 lines]
> Doesn't the call with the literal 2 fall under the same rules as stated
> above call with 0?

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

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too

Jay B. Harlow [MVP - Outlook] - 16 Aug 2004 06:05 GMT
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

> Maybe I'm confused. The two calls below. 2 is not a literal but 0 is?
>
[quoted text clipped - 159 lines]
> > > > > Thanks,
> > > > > Chris
JD - 16 Aug 2004 12:29 GMT
Aaah...default value for Value types. Thanks Jay and John.

> JD,
> Me bad. I miss read your question.
[quoted text clipped - 199 lines]
> > > > > > Thanks,
> > > > > > Chris
Plausible Indirection - 17 Aug 2004 19:25 GMT
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

> Aaah...default value for Value types. Thanks Jay and John.
Jon Skeet [C# MVP] - 17 Aug 2004 19:46 GMT
> 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.

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?

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too

Plausible Indirection - 18 Aug 2004 15:54 GMT
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

> > 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
[quoted text clipped - 4 lines]
> the default value for an enum type is 0. Or are you talking about
> overload resolution?
Jon Skeet [C# MVP] - 18 Aug 2004 17:23 GMT
> 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.

<snip>

> 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 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.

> It seems that the creators of VB have lumped enumerations in with
> integers, just a more specific variety, most likely because the
[quoted text clipped - 12 lines]
>
> assigns the value Bar1 to test.

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.

> It seems sloppy to me that two enumerations that are entirely
> different, say Apples and Oranges, can be added or compared.  For more
[quoted text clipped - 9 lines]
>
> which assigns the value True to test.

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

> 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.

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

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too

cody - 18 Aug 2004 08:06 GMT
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
> 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
[quoted text clipped - 3 lines]
>
> > Aaah...default value for Value types. Thanks Jay and John.
cody - 16 Aug 2004 08:18 GMT
> 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
Jon Skeet [C# MVP] - 16 Aug 2004 09:06 GMT
> > As I stated the enumerated type is more specific then an Integer!
>
> Why is it more specific? They both inherit from valuetype.

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

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too

cody - 16 Aug 2004 09:24 GMT
> > Why is it more specific? They both inherit from valuetype.
>
> It's more applicable because there is a widening conversion from the
> enum to Integer, but no widening conversion from Integer to enum.

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
> > > As I stated the enumerated type is more specific then an Integer!
> >
[quoted text clipped - 7 lines]
> http://www.pobox.com/~skeet
> If replying to the group, please do not mail me too
Jon Skeet [C# MVP] - 16 Aug 2004 10:19 GMT
> > > Why is it more specific? They both inherit from valuetype.
> >
> > It's more applicable because there is a widening conversion from the
> > enum to Integer, but no widening conversion from Integer to enum.
>
> what do you mean with widening conversion?

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>

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too

Jay B. Harlow [MVP - Outlook] - 16 Aug 2004 14:36 GMT
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

> > > Why is it more specific? They both inherit from valuetype.
> >
[quoted text clipped - 20 lines]
> > http://www.pobox.com/~skeet
> > If replying to the group, please do not mail me too
Jon Skeet [C# MVP] - 16 Aug 2004 14:47 GMT
> 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>

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too

Jay B. Harlow [MVP - Outlook] - 16 Aug 2004 15:46 GMT
Jon,
That's true, I simplified my statement.

Jay

> > A widening conversion is one where there is no loss of data.
>
[quoted text clipped - 7 lines]
> Widening conversions never overflow but may entail a loss of precision.
> </quote>
Plausible Indirection - 17 Aug 2004 18:12 GMT
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

> Code,
> A widening conversion is one where there is no loss of data. Integer allows
[quoted text clipped - 13 lines]
> Hope this helps
> Jay
Jay B. Harlow [MVP - Outlook] - 18 Aug 2004 14:45 GMT
Chris,
> 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.
Unfortunately with VS.NET 2002 & VS.NET 2003 you will be losing some hair.
:-|

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!

> On top of that, I tried
>
>             dt = New Foo(CInt(0), Bar.Bar0)
Try

   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.

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

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

> 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!
[quoted text clipped - 53 lines]
> > Hope this helps
> > Jay

Rate this thread:







Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.