Why do enum values req explicit conversions

  • Thread starter Thread starter Michael C
  • Start date Start date
M

Michael C

If a class inherits from another class, say Form inherits from control, then
I can assign the Form to a variable of type Control without needing an
explicit conversion, eg

Form1 f = new Form1();
Control c = f;

An enum value inherits from int but it doesn't get implicitly converted:

HorizontalAlignment h = HorizontalAlignment.Center;
int i = h;

Any reason?

Thanks,
Michael
 
The difference is that Form is a subclass of Control. A basic rule of
inheritance (what is that guy's name again?) is that the subclass can always
be substituted for the superclass.

The problem is that an enum is not a subclass of an int (or long or sbyte or
uint, etc..). At run time, enums are treated as boxed types of the
underlying enum type. That is why you must unbox it to get the integer
value.

HTH

Dale Preston
MCAD, MCDBA, MCSE
 
enums are boxed at run time? That doesn't seem right to me. (But I
could be wrong. :)

I was going to say that it's more a question of self-documenting code.
One really ought to treat an enumeration as a self-contained type, even
though it is, underneath it all, just an int (or whatever). So, if you
want to start manipulating it as if it were a number, you have to cast
in order to raise a flag to the reader: "I'm doing something unusual
here! Pay attention!"

Other than that I can't see any reason.
 
From the C# specification:

At run-time, a value of type System.Enum can be null or a reference to a
boxed value of any enum type.

Dale
 
Dale Preston said:
From the C# specification:

At run-time, a value of type System.Enum can be null or a reference to a
boxed value of any enum type.

Are you confusing the enum itself with the elements in it? (It's confusing
me a little). The actual enum inherits from enum but the elements don't as
far as I can tell. On the other hand it's possible to set one of the
elements to a variable of type System.Enum implicitly.

Michael
 
The enum elements inherit from System.Enum.


Michael C said:
Are you confusing the enum itself with the elements in it? (It's confusing
me a little). The actual enum inherits from enum but the elements don't as
far as I can tell. On the other hand it's possible to set one of the
elements to a variable of type System.Enum implicitly.

Michael
 
Another thing to keep in mind, enums can use, as the underlying type (not
the base type, only the numeric representation type) an integer, long, byte,
uint, short, ushort, etc

You can't expect to implicitly convert to an int.

Dale
 
Dale Preston said:
The enum elements inherit from System.Enum.

I suspected this might be the case even though the object browser says it's
not. So it is inherited from Enum and NOT from int? It can be explicitly
converted to int but it's not actually an int? It's a bit misleading the
syntax then:

Public Enum ABC : int
{
....
}
Another thing to keep in mind, enums can use, as the underlying type (not
the base type, only the numeric representation type) an integer, long,
byte,
uint, short, ushort, etc

You can't expect to implicitly convert to an int.

There's no reason it shouldn't be possible

Michael
 
Michael C said:
I suspected this might be the case even though the object browser says
it's not. So it is inherited from Enum and NOT from int? It can be
explicitly converted to int but it's not actually an int? It's a bit
misleading the syntax then:

Public Enum ABC : int
{
...
}

It is, but what other way is there that doesn't cause extra complexity?
There's no reason it shouldn't be possible

Explicit conversion is required for consistency(Technically you are losing
information, however litttle and easily regained) and to avoid making silly
mistakes.

0 has an implicit conversion to any enum, all other literals and enums must
be converted.
 
Dale Preston said:
From the C# specification:

At run-time, a value of type System.Enum can be null or a reference to a
boxed value of any enum type.

That is because System.Enum *IS* a reference type, while derivatives are
value types. You will see a similar dichotomy in System.ValueType. A value
of any given enum is just a value on the stack(literally the base type as I
don't believe enums have any other fields), but when put into a System.Enum
variable they must be boxed.
 
Daniel O'Connell said:
It is, but what other way is there that doesn't cause extra complexity?

I guess it makes sense if you think of it as a parameter to specify how an
object is defined, as opposed to what it inherits from.
Explicit conversion is required for consistency(Technically you are losing
information, however litttle and easily regained) and to avoid making
silly mistakes.

That makes sense.

Michael
 
Yes, this is true. Enumerated values are not boxed unless you store
them in a variable declared as System.Enum. I created the following
code as a test:

public class ConsoleClass
{
public ConsoleClass()
{ }

public enum Enumeration
{
Nothing,
FirstValue,
SecondValue,
ThirdValue
}

[STAThread]
public static void Main(string[] argc)
{
Enumeration a = Enumeration.Nothing;
Enumeration b = Enumeration.SecondValue;
Enumeration c = a | b;
Enum d = a;
}
}

The resulting intermediate language code looks like this:

..method public hidebysig static void Main(string[] argc) cil managed
{
.entrypoint
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor() =
( 01 00 00 00 )
// Code size 16 (0x10)
.maxstack 2
.locals init ([0] valuetype
ConsoleApplication1.ConsoleClass/Enumeration a,
[1] valuetype ConsoleApplication1.ConsoleClass/Enumeration
b,
[2] valuetype ConsoleApplication1.ConsoleClass/Enumeration
c,
[3] class [mscorlib]System.Enum d)
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: ldc.i4.2
IL_0003: stloc.1
IL_0004: ldloc.0
IL_0005: ldloc.1
IL_0006: or
IL_0007: stloc.2
IL_0008: ldloc.0
IL_0009: box ConsoleApplication1.ConsoleClass/Enumeration
IL_000e: stloc.3
IL_000f: ret
} // end of method ConsoleClass::Main

Notice that there is only one box operation: when I assign a, which is
declared as an Enumeration, to d, which is declared as System.Enum.
It's just like assigning an int to a System.Object. Even though int
inherits from System.Object, ints are not held as boxed values unless
you actually assign one to a variable of type System.Object.
 
Notice that there is only one box operation: when I assign a, which is
declared as an Enumeration, to d, which is declared as System.Enum.
It's just like assigning an int to a System.Object. Even though int
inherits from System.Object, ints are not held as boxed values unless
you actually assign one to a variable of type System.Object.

It makes a lot more sense - while at the same time becoming more
confusing - when you read the CIL spec.

The value type "System.Int32" itself *doesn't* inherit from
System.Object (directly or indirectly). However, there is another type,
also called "System.Int32" which *does* inherit from System.Object -
and that's the boxed version.

Basically, every value type has a reference type of the same name which
is the boxed form. That's the one which actually derives from
System.ValueType or System.Enum.

There's no way that I know of of getting references to two different
Type objects, one for the boxed version and one for the value type
version though...
 
This is from the C# specification in the Visual Studio documentation:

"The type System.Enum is the abstract base class of all enum types (this is
distinct and different from the underlying type of the enum type), and the
members inherited from System.Enum are available in any enum type."

If enums don't inherit from System.Enum, then what exactly does "abstract
base class" mean in that context? Note that the statement says that the
base class is "distinct and different" from the underlying type of the enum.
The underlying type of the enum is returned by the TypeCode returned from
the instance GetTypeCode method. If the enum does not inherit from
System.Enum, then how is it that the instance enum gets the GetTypeCode
method? It certainly doesn't get it from the underlying type. Int32
doesn't have a GetTypeCode method. And Int32 is sealed and cannot be
inherited from.

From the .Net Framework documentation of the System.Enum class:

"Class Enum is derived from class ValueType; that is, Enum is itself a
reference type, not a value type."

Because Enum is a reference type, it must be unboxed to get the integral
underlying type. Again, from the C# specification:

"an unboxing conversion (Section 4.3.2) exists from System.Enum to any enum
type."

While many of the guesses about why you have to explicitly cast to get the
integer make logical sense and might be good reasons for the requirement,
they are not supported either by the language specification, by the
framework documentation, or by the object-oriented principals of
inheritance.

Dale
 
Daniel Jin said:
back to op, why require the cast? my guess is for type safety. so enum
values are not accidentally passed to a method expecting integer
parameter.

Originally I was trying to replace this code with a function

if ((SomeEnumValue & SomeEnum.SomeFlag) != 0)
{
}

with a function, something like

if(EnumContainsFlag(SomeEnumValue, SomeEnum.SomeFlag))

But I can't pass them in as System.Enum becuase I can't convert them to int
and I can't pass them in as int without converting them.

Michael
 
There's another reason why you wouldn't want to do that, which is that
most C# developers will be accustomed to seeing the first idiom:

if ((SomeEnumValue & SomeEnum.SomeFlag) != 0)
{
}

so if you change the syntax by using a method instead of testing in
open code like this, it will take anyone maintaining the code an extra
second or two to remember what the function does.

Yes, the "standard" syntax isn't pretty, but it _is_ the standard, and
it's what most people will be expecting to see.
 
You mean there was a point to all this debate? *laughing*.

You will have to do the conversion like in the following example:

public static void Main(String[] args)
{
TestEnum myTest;
myTest = TestEnum.enum2;
myMethod((int)myTest);
}

public static void myMethod(int enumValue )
{
Console.WriteLine(enumValue.ToString());
Console.ReadLine();
}

public enum TestEnum
{
enum1,
enum2,
enum3
}

// Or you can change it to pass the enum and do the conversion in your
method:

public static void Main(String[] args)
{
TestEnum myTest;
myTest = TestEnum.enum2;
myMethod(myTest);
}

public static void myMethod(TestEnum enumValue )
{
Console.WriteLine(((int)enumValue).ToString());
Console.ReadLine();
}


Hope that helps,

Dale Preston
 
Dale Preston said:
This is from the C# specification in the Visual Studio documentation:

"The type System.Enum is the abstract base class of all enum types (this is
distinct and different from the underlying type of the enum type), and the
members inherited from System.Enum are available in any enum type."

If enums don't inherit from System.Enum, then what exactly does "abstract
base class" mean in that context? Note that the statement says that the
base class is "distinct and different" from the underlying type of the enum.
The underlying type of the enum is returned by the TypeCode returned from
the instance GetTypeCode method. If the enum does not inherit from
System.Enum, then how is it that the instance enum gets the GetTypeCode
method? It certainly doesn't get it from the underlying type. Int32
doesn't have a GetTypeCode method. And Int32 is sealed and cannot be
inherited from.

The boxed type (the reference type) representing any enum is derived
from System.Enum. The value type doesn't.

It's exactly the same as System.Int32 deriving from System.ValueType,
despite System.ValueType being a reference type in itself. That doesn't
mean that ints always need to be boxed and unboxed - they only do when
there's a conversion to System.ValueType or System.Object.
From the .Net Framework documentation of the System.Enum class:

"Class Enum is derived from class ValueType; that is, Enum is itself a
reference type, not a value type."

Because Enum is a reference type, it must be unboxed to get the integral
underlying type.

Only if you actually *have* an expression of type Enum rather than of
the specific enum type in the first place.
Again, from the C# specification:

"an unboxing conversion (Section 4.3.2) exists from System.Enum to any enum
type."

Also from the C# specification:

<quote>
The explicit enumeration conversions are:
[...]
From any enum-type to sbyte, byte, short, ushort, int, uint, long,
ulong, char, float, double, or decimal.
</quote>

No mention of boxing there, because there's no need for one.
While many of the guesses about why you have to explicitly cast to get the
integer make logical sense and might be good reasons for the requirement,
they are not supported either by the language specification, by the
framework documentation, or by the object-oriented principals of
inheritance.

Not entirely sure what you mean here, but I believe you're wrong if
you're basing that statement on the idea that enum values are always
boxed.

The language specification *could* have been written such that the
enumeration conversions were implicit rather than explicit - but type
safety would have been lost.
 
Jon Skeet said:
The boxed type (the reference type) representing any enum is derived
from System.Enum. The value type doesn't.

There is no value type of enum. There is an underlying TypeCode that is
used for integral representation of the enum. To get the value type, you
must unbox it. If it were already an int, it wouldn't be necessary to cast
an int as an int. But if you box an int, it is necessary to unbox to get
back to the int. The syntax for unboxing is, coincidentally, the same as
the syntax for an explicit cast.

It's exactly the same as System.Int32 deriving from System.ValueType,
despite System.ValueType being a reference type in itself. That doesn't
mean that ints always need to be boxed and unboxed - they only do when
there's a conversion to System.ValueType or System.Object.

System.Int32 is a value type inheriting from System.ValueType. System.Enum
is a reference type inheriting from System.ValueType. Since we seem to
agree that System.Enum is a reference type, yet it inherits from a value
type, then how did it get to be a reference type? As I first quoted from
the C# specification, it is boxed at runtime. That's the only way to get a
reference type out of a value type.
Only if you actually *have* an expression of type Enum rather than of
the specific enum type in the first place.


All specific enum instances are of type Enum. Subclasses are always of the
type of the superclass. Basic OOP.
Again, from the C# specification:

"an unboxing conversion (Section 4.3.2) exists from System.Enum to any enum
type."

Also from the C# specification:

<quote>
The explicit enumeration conversions are:
[...]
From any enum-type to sbyte, byte, short, ushort, int, uint, long,
ulong, char, float, double, or decimal.
</quote>

There are a lot of explicit conversions possible in the .Net classes. They
do not imply inheritance by the existence of the explicit conversion. In
fact, since a subclass can always be substituted for a superclass, the
opposite could be inferred though it doesn't always hold true.

No mention of boxing there, because there's no need for one.


Not entirely sure what you mean here, but I believe you're wrong if
you're basing that statement on the idea that enum values are always
boxed.

Well, there has been argument that enum instances do not inherit from
System.Enum. Again, if they do not, then how do they get the instance
methods of System.Enum? They do not get the instance methods of
System.Int32.


The language specification *could* have been written such that the
enumeration conversions were implicit rather than explicit - but type
safety would have been lost.

But it wasn't.




Dale
 
Bruce Wood said:
There's another reason why you wouldn't want to do that, which is that
most C# developers will be accustomed to seeing the first idiom:

if ((SomeEnumValue & SomeEnum.SomeFlag) != 0)
{
}

so if you change the syntax by using a method instead of testing in
open code like this, it will take anyone maintaining the code an extra
second or two to remember what the function does.

Yes, the "standard" syntax isn't pretty, but it _is_ the standard, and
it's what most people will be expecting to see.

I don't think it would be much of a problem and once they worked it out it
would be clearer to them. With the above format is't easy to mix up the
brackets and & signs, especially when there are several checks in an if
statement. But that would only be the case if it were possible to write it
in a simple form, which it doesn't look possible.

Michael
 
Back
Top