Enumerated Type Width? - c#

Quick question. How wide are enumerated types? Are they of the minimum width required to represent the enumeration or are all enums ints? If they're ints, can you change the width of enums or would you have to type cast for each occurrence?

(This is for C++)
From the standard:
The underlying type of an enumeration
is an integral type that can represent
all the enumerator values defined in
the enumeration. It is
implementation-defined which integral
type is used as the underlying type
for an enumeration except that the
underlying type shall not be larger
than int unless the value of an enumerator
cannot fit in an int or
unsigned int. If the enumerator-list
is empty, the underlying type is as if
the enumeration had a single
enumerator with value 0. The value of
sizeof() applied to an enumeration
type, an object of enumeration type,
or an enumerator, is the value of
sizeof() applied to the underlying
type.
In C++0x you can define the underlying type of an enum like follows:
enum foo : unsigned int { bar, baz };
Also note that the new strongly typed enums ("enum class") have a default underlying type of int.

In C and C++ they're ints. Type casting will not change them in the slightest, unless you change them to a narrower integer type (char, short).

This is different for the languages you tagged this question with.
In C and C++03, the underlying type of an enum is implementation defined. In C++0x, we can declare the underlying type ourselfs, called strongly typed enums (or enum classes):
// declares an enum with underlying type `unsigned char`
enum class MyEnum : unsigned char {/*...*/};

In C# you can specify the underlying type, and if you don't specify then the default is Int32.
public enum ThirtyTwoBitsWide
{
This, Is, The, Default, Size
}
public enum EightBitsWide : byte
{
Explicitly, Specify, The, Underlying, Size
}

Related

Conversion of numeric strings to enum in C#

Recently I've discovered that any string that contains only numeric characters can be converted to enum in C#. For example, imagine there is an enum defined as follows:
public enum TestEnum
{
One = 1,
Two = 2,
Three = 3
};
I can perform a conversion of some random numeric string to TestEnum.
TestEnum testEnum = (TestEnum)Enum.Parse(typeof(TestEnum), "4");
Console.WriteLine(testEnum);
The value '4' of course does not get mapped to one of defined TestEnum values and the output would be just 4, however there would be no error, so this conversion is legal.
On the other hand, if I try to check whether this value defined in TestEnum:
Console.WriteLine(Enum.IsDefined(typeof(TestEnum), "4"));
I would receive False in the output.
This seems a little strange to me, taking into account that the following conversion of non-numeric string, e.g.
(TestEnum)Enum.Parse(typeof(TestEnum), "Test")
will throw System.ArgumentException.
I am confused with such behavior. Was it intentionally designed this way or is this just a coincidence?
Was it intentionally designed this way or is this just a coincidence?
I'm not sure I understand your question. The documentation seems completely clear to me:
Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
(Emphasis mine)
It also says:
If value is the string representation of an integer that does not represent an underlying value of the enumType enumeration, the method returns an enumeration member whose underlying value is value converted to an integral type. If this behavior is undesirable, call the IsDefined method to ensure that a particular string representation of an integer is actually a member of enumType
Clearly this is intentional behavior, and not just coincidental.
Note that you can always cast any value of the enum's underlying type (int in this case), to the enum type itself. The enum type feature does not enforce values of the enum to correspond to named value. So the parsing behavior is entirely consistent with the compile-time behavior of enum types.
I suppose one might debate whether the enum type should allow unnamed values to be used. But, given that one of the features of enum types is to allow for flag-based enums, whether or not they have the [Flags] attribute, and given that it would be inconvenient (to say the least) to have to name every single combination of every single flag one defines in an enum type, it seems reasonable that they would go ahead and allow unnamed values for all types of enum types.

Why must I use an explicit cast when using an enum in place of its underlying type?

public enum Foo : byte
{
BAR = 0x00,
BAZ = 0x01,
DERP = 0xFF
}
public void AppendAsHex(StringBuilder sb, byte b)
{
sb.AppendFormat("{0:X}", b);
}
Why does this demand an explicit cast?
Foo theDerp = Foo.DERP;
AppendAsHex(sb, (byte)theDerp); // Fine
AppendAsHex(sb, theDerp); // Compile Error
No loss of precision can occur. The method declares it only wants a byte, disregarding any enum goodness.
EDIT
This works fine if we trade the enum for a byte and make the function take another numeric type, eg:
public void AppendAsHex(StringBuilder sb, uint u)
{
sb.AppendFormat("{0:X}", u);
}
byte b = 21;
AppendAsHex(sb, b); // Fine
So, the compiler will promote a numeric type to a larger numeric type without any fuss, but demands a cast to do the same with an enum:byte to byte.
Clearly an enum:byte is not technically of Type byte, but surely the compiler could see it's of type System.Enum and check the type of the values contained in the enum?
While it makes perfect sense if using complex types the compiler may not be able to size up, in this case the compiler is fully aware of everything. I don't see how, if primitives can be promoted, the compiler would refuse to promote/cast something explicitly declared as a primitive.
It seems inconsistent to me and I'd like to understand it better.
Very simply because C# is strongly typed. An enum value is not of type byte even if you set it's translated values as byte, so you must cast it as byte before you can use it with a function that is expecting type byte. It's no different than casting another type.
Also, if your focus is on keeping things clean, you could consider rewriting (or overloading) your method slightly so that the cast is invisible to everything outside of it. It doesn't change the solution, but assuming you will be reusing the method in more than one place, it is less code:
public void AppendAsHex(StringBuilder sb, Foo b)
{
AppendAsHex(sb, (byte)b);
}
public void AppendAsHex(StringBuilder sb, byte b)
{
sb.AppendFormat("{0:X}", b);
}
At which point, this would work
Foo theDerp = Foo.DERP;
AppendAsHex(sb, theDerp);
The underlying type specifies how much storage is allocated for each
enumerator. However, an explicit cast is necessary to convert from
enum type to an integral type.
https://msdn.microsoft.com/en-us/library/sbbt4032(v=vs.140).aspx
So, despite the use of : to declare the underlying type, the actual base type of any enum is System.Enum. That's why it still needs explicit cast.
Check out the answer here.
An enum cannot inherit from anything but System.Enum so all the ': byte' is doing is changing how the values of an enum are represented.

Enum value conversion

What is the goal of explicitly casting an enum to it's declared type in, for example,
enum MyEnum : long
{
Zero,
One,
};
long one = (long)MyEnum.Zero; // <-- explicit conversion
Is there a logical explanation for this?
There are a few reasons but the main one that I use it for is when saving the field to a DB. The column in the DB has a data type that is int (in my case) and I use the conversion to allow me to save the enum value in the table.
The underlying type of enum (long in your case) specifies only how much storage is allocated for each enumerator. The type of enum itself is System.Enum so you need explicit cast.
That declaration tells compiler that you will use large values, but enum is still enum and it will not implicitly convert to any type. It's to be sure you know what you're doing, I think.
From C# Reference http://msdn.microsoft.com/pl-pl/library/sbbt4032.aspx
In the following example, the base-type option is used to declare an enum whose members are of type long. Notice that even though the underlying type of the enumeration is long, the enumeration members still must be explicitly converted to type long by using a cast.

where t : class generic constraint and const value declaration

According to C# specification in 10.4 Constants:
The type specified in a constant declaration must be sbyte, byte,
short, ushort, int, uint, long, ulong, char, float, double, decimal,
bool, string, an enum-type, or a reference-type. Each
constant-expression must yield a value of the target type or of a type
that can be converted to the target type by an implicit conversion
(§6.1).
Why then I can't do following:
public class GenericClass<T>
where T : class
{
public const T val = null;
}
That should be possible, because:
where T : class means, that The type argument must be a reference type; this applies also to any class, interface, delegate, or array type (from MSDN)
it satisfies another words from specification: the only possible value for constants of reference-types other than string is null.
Any possible explanation?
Possible Explanation
Consider how the CLR initializes static members of generic classes or when it invokes static constructors on generic types. Normally, static initialization occurs when the program is first loaded; however, generic classes initialize their static members the first time an instance of that class is created.
Bear in mind that a generic class is not a single type; every single T that gets passed in the type declaration is creating a new type.
Now then consider a const expression, which has the requirement to be evaluated at compile-time. Although T is constrained as a class, and therefore it can receive the value null, the variable val does not exist in memory until the class has been created at runtime.
For example, consider if the const T val were valid. Then elsewhere in the code we could use:
GenericClass<string>.val
GenericClass<object>.val
Edit
Although both expressions would have the value null, the former is of type string and the latter is of type object. In order for the compiler to perform substitution, it needs to know the type definitions of the constants in question.
Constraints may be enforced at compile-time, but open generics are not converted into closed generics until runtime. Therefore, GenericClass<object>.val cannot be stored in the compiler's local memory to perform the substitution because the compiler does not instantiate the closed form of the generic class, and thus does not know what type to instantiate the constant expression to.
Eric Lippert admitted it is a bug, and it should be allowed:
It looks to me like you’ve found a bug; either the bug is in the specification, which should explicitly call out that type parameters are not valid types, or the bug is in the compiler, which should allow it.

Are enum types stored as ints in C#?

Are enum types stored as ints in C#?
Will the enum below be represented as 0, 1, 2?
If not, what's the lightest way to define an enum?
public enum ColumnType
{
INT,STRING,OBJECT
}
From the MSDN
The default underlying type of enumeration elements is int.
By default, the first enumerator has the value 0, and the value of each successive enumerator is increased by 1.
So your assumptions are correct. Enums are stored as ints and your example will be represented as 0, 1, 2. However, you shouldn't rely on this and always refer to them by their assigned name just in case someone overrides the default.
Yes, an enum is stored as an int by default., as indicated here.
You can also specify the underlying type, e.g.:
enum Foo : short
{
Bar,
Baz
}
Although that usually isn't necessary.
By default yes:
http://msdn.microsoft.com/en-us/library/sbbt4032(v=vs.80).aspx
The enum keyword is used to declare an enumeration, a distinct type
consisting of a set of named constants called the enumerator list.
Every enumeration type has an underlying type, which can be any
integral type except char. The default underlying type of the
enumeration elements is int. By default, the first enumerator has the
value 0, and the value of each successive enumerator is increased by
1.
The default underlying type for an enum is an int, but different types can be used explicitly. For example, you can use byte:
enum Days : byte {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};
See http://msdn.microsoft.com/en-us/library/sbbt4032.aspx for details.
Every enumeration type has an underlying type, which can be any integral type except char.
Yes your enum be represented as 0,1,2 by default and it is the lightest way to define an enum
How ever you define enum by defining starting value as your requirement and assigning integral values explicitly to each of your enum member as well
The point about underlying types is already well-covered, so I'll add a different context:
At the IL / runtime level, instances of enums do not exist. They only exist:
a: to the compiler
b: to the runtime as a boxed value
c: in metadata (parameter/field/variable declarations)
At all other times, they are purely the integer-types values (most commonly int). The compiler uses enum variables (etc) for static analysis, overload resolution, etc but the opcodes it emits are identical to those it would have emitted for constant / literal integer values; i.e.
SomeEnum foo = SomeEnum.WithValueOne;
int bar = 1;
will have identical IL (except for the variable type, perhaps).
The only time it gets interesting is if emitting a box operation, or a call like .ToString() etc.
So: as long as you don't box them (store them in an object field/etc), then there is (at runtime) no significant difference between an enum or an integer when it comes to storage or performance (unless you use .ToString(), etc)

Categories

Resources