I have three enums:
enum ValueType : int
{
FloatingPoint = 2,
.../...
}
enum ConstraintType : int
{
Range = 2,
.../...
}
enum Parameter : int
{
ExposureTime = F(ValueType.FloatingPoint, ConstraintType.Range, 23),
.../...
}
The problem is in the signature of F if I use:
private static int F(ValueType _V, ConstraintType _C, int _N) { ... }
I get an error (invalid arguments) for every call in the definition of Parameter, but if I use the following instead:
private static int F(int _V, int _C, int _N) { ... }
Everything is fine.
It's not a blocking problem, but I'd like to understand why is that.
The C# spec states in section 14.3 ("Enum members") that
Within an enum member initializer, values of other enum members are
always treated as having the type of their underlying type, so that
casts are not necessary when referring to other enum members.
As far as I can tell this is why the arguments appear to have a type of int. It's interesting to note that this will not result in an invalid argument error:
ExposureTime = F((ValueType)ValueType.FloatingPoint,
(CostraintType)ConstraintType.Range,
23),
Of course it will still result in another error because you cannot use a method call to initialize enum members as Marc says. A method call is not a constant expression, while
The associated value of an enum member is assigned either implicitly
or explicitly. If the declaration of the enum member has a
constant-expression initializer, the value of that constant
expression, implicitly converted to the underlying type of the enum,
is the associated value of the enum member. If the declaration of the enum member has no initializer, its associated value is set implicitly [...]
For enums with explicit values, the value must be a constant expression. F(...) is not a constant expression. Regardless of whether the parameters are int or enums, you cannot assign an enum value from a function call.
Your "everything is fine" actually means:
The expression being assigned to 'blah.Parameter.ExposureTime' must be constant
The only "problem" here is that the compiler doesn't give a very elegant error message to a particular illegal scenario.
Related
I came from C++ background and have a simple problem here. I have the following List as a class member:
public enum FieldColor {Empty, Blue, White};
private List<List<FieldColor>> m_board = new List<List<FieldColor>>();
Now I have such a simple method:
private FieldColor GetFieldColor(GridCoordinates coordinates) {
return m_board[coordinates.row][coordinates.column];
}
Now I want to use this method to assign value to a coordinate and I have wrote this:
GetFieldColor(coordinates) = color;
And I get this error:
The left-hand side of an assignment must be a variable, property or
indexer [Assembly-CSharp]
What is the solution.
You could always use a Ref return
Starting with C# 7.0, C# supports reference return values (ref
returns). A reference return value allows a method to return a
reference to a variable, rather than a value, back to a caller. The
caller can then choose to treat the returned variable as if it were
returned by value or by reference. The caller can create a new
variable that is itself a reference to the returned value, called a
ref local.
Limitations
There are some restrictions on the expression that a method can return
as a reference return value. Restrictions include:
The return value must have a lifetime that extends beyond the execution of the method. In other words, it cannot be a local variable
in the method that returns it. It can be an instance or static field
of a class, or it can be an argument passed to the method. Attempting
to return a local variable generates compiler error CS8168, "Cannot
return local 'obj' by reference because it is not a ref local."
The return value cannot be the literal null. Returning null generates compiler error CS8156, "An expression cannot be used in this
context because it may not be returned by reference."
A method with a ref return can return an alias to a variable whose value is currently the null (uninstantiated) value or a nullable type
for a value type.
The return value cannot be a constant, an enumeration member, the by-value return value from a property, or a method of a class or
struct. Violating this rule generates compiler error CS8156, "An
expression cannot be used in this context because it may not be
returned by reference."
Example
private ref int GetFieldColor(GridCoordinates coordinates)
{
return ref m_board[coordinates.row][coordinates.column];
}
Valid usage
GetFieldColor(gridCoordinates) = 345;
Or you could user an indexer
Indexers allow instances of a class or struct to be indexed just like
arrays. The indexed value can be set or retrieved without explicitly
specifying a type or instance member. Indexers resemble properties
except that their accessors take parameters.
public int this[GridCoordinates coordinates]
{
get => m_board[coordinates.row][coordinates.column];
set => m_board[coordinates.row][coordinates.column] = value;
}
Usage
this[GridCoordinates] = 345
You will need to create a separate setter method for the same
private FieldColor SetFieldColor(GridCoordinates coordinates, FieldColor color) {
m_board[coordinates.row][coordinates.column] = color;
}
You can also overload the [] operator to fill your needs. I find this approach less hacky than a ref return and more in the C# philosophy.
public FieldColor this[int row, int column]
{
get => m_board[row][column];
set => m_board[row][column] = value;
}
If needed, a second overload which uses the previous one:
public FieldColor this[GridCoordinates coordinates]
{
get => this[coordinates.row, coordinates.column];
set => this[coordinates.row, coordinates.column] = value;
}
Usage:
field[0, 1] = FieldColor.Empty;
var color = field[0, 1];
I have the following cast:
int myInteger = (int)myItem.EnumValue;
Where the enum is:
public enum EnumValue
{
Yes= 0,
No = 1
}
From my observations it seems that when the EnumValue on myItem is not set, the value of EnumValue is on default set to Yes and subsequently cast to 0.
Why is the enum not null? Is my observation correct and why is it so?
Enum's are value types, so just like int's and other value types, they cannot be null. Instead their default value is the equivalent of 0 (since you've assigned an enum value of Yes to 0, that's the default value in this case).
If you want a nullable enum, use Nullable<EnumValue> or EnumValue? for short.
Further Reading
Value Types (C# Reference)
enum (C# Reference)
Nullable Types (C# Programming Guide)
Because enum variable is essentially a variable of enum underlying type. By default it is integer. Integer cannot have null value.
UPDATE: Under hood enum looks like
class public auto ansi sealed EnumValue extends [mscorlib]System.Enum
{
.field public static literal EnumValue Yes = int32(0x00000000)
.field public static literal EnumValue No = int32(0x00000001)
.field public specialname rtspecialname int32 value__
}
I.e. its a set of constants (which are inlined during compilation) and integer (by default) field which holds enum value. Thats why you can assign any integer value to variable of EnumValue type. That's why by default it has value 0. That's why it cannot be null.
Several answers suggest using a nullable enum, but you can also build similar logic into your enum without the overhead of the Nullable type:
public enum EnumValue
{
Unknown = 0,
Yes = 1,
No = 2
}
Since the enum's default value is 0, the default value is Unknown. Note that the default value is zero even if you don't define a field with the value zero, like this:
public enum EnumValue
{
Yes = 1,
No = 2
}
Why is enum not null?
Because enum is a value type, which can never be null.
The underlying type of an enumeration is always integral. From the docs:
Every enumeration type has an underlying type, which can be any
integral type except char. The default underlying type of enumeration
elements is int.
Since yours is based on int, the default value of the enum is the default value of an integer - which is zero.
I have this extension method for an Enum:
public static List<Enum> Values(this Enum theEnum)
{
return Enum.GetValues(theEnum.GetType()).Cast<Enum>().ToList();
}
I'm getting a code analysis violation:
CA1062 Validate arguments of public methods
In externally visible
method 'EnumExtensions.Values(this Enum)', validate parameter
'theEnum' before using it.
Why is that happening? How can I validate the parameter? I can't check for null because an enum is a non-nullable value type. Is there some other check that is supposed to be occurring here?
I can't check for null because an enum is a non-nullable value type.
Any particular enum is a value type, but Enum itself isn't. (Just like ValueType isn't a value type either... every type derived from ValueType except Enum is a value type.)
In other words, I could write:
Enum foo = null;
var bang = foo.GetValues();
That would compile and then fail at execution time with a NullReferenceException.
Given that you ignore the value except to get its type, I'd actually suggest removing it and either accepting a Type or making it generic in the type of enum you want. But if you want to keep the current signature, you just need:
if (theEnum == null)
{
throw new ArgumentNullException();
}
You might also want to look at my Unconstrained Melody project which provides a bunch of helper methods for enums, generically constrained to enum types via IL manipulation.
The enum keyword is used to declare an enumeration, a distinct type that consists of a set of named constants called the enumerator list.
It is used just for declare another enums. In any case, the input should be your declaration.
public enum theEnum {
enum1,
enum2
}
public void ShowEnum(theEnum e)
{
System.Console.WriteLine(e.GetType());
}
How can I call a method that expects an int value by using an enum member. I dont want the called method to have to know about the enum.
public enum Volume : int
{
Low = 1,
Medium = 2,
High = 3
}
public void Start() {
DoSomeWork(Volume.Low); //this complains
//this works DoSomething((int)Volume.Low);
}
public void DoSomeWork(int vol) {
//Do something
}
Cast it explicitly to int (as you have already figured out):
DoSomeWork((int)Volume.Low)
Implicit conversion from enum to underlying type is forbidden, since there are a lot of cases when this conversion does not make sense. #EricLippert explains this well enough here.
However why introduce the enum if you are not using it? If the volume rate in your program is specified by the enum - then this is the type your method should expect as a parameter.
Call it like this:
DoSomeWork( (int) Volume.Low );
Why not using this way:
public void DoSomeWork(Volume volume) {
//Do something
}
as documentation states
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.
so you can just simple cast it to int and pass it to the method that way.
DoSomeWork((int)Volume.Low);
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is it safe for structs to implement interfaces?
Take this code:
interface ISomeInterface
{
public int SomeProperty { get; }
}
struct SomeStruct : ISomeInterface
{
int someValue;
public int SomeProperty { get { return someValue; } }
public SomeStruct(int value)
{
someValue = value;
}
}
and then I do this somewhere:
ISomeInterface someVariable = new SomeStruct(2);
is the SomeStruct boxed in this case?
Jon's point is true, but as a side note there is one slight exception to the rule; generics. If you have where T : ISomeInterface, then this is constrained, and uses a special opcode. This means the interface can be used without boxing. For example:
public static void Foo<T>(T obj) where T : ISomeInterface {
obj.Bar(); // Bar defined on ISomeInterface
}
This does not involve boxing, even for value-type T. However, if (in the same Foo) you do:
ISomeInterface asInterface = obj;
asInterface.Bar();
then that boxes as before. The constrained only applies directly to T.
Yes, it is. Basically whenever you need a reference and you've only got a value type value, the value is boxed.
Here, ISomeInterface is an interface, which is a reference type. Therefore the value of someVariable is always a reference, so the newly created struct value has to be boxed.
I'm adding this to hopefully shed a little more light on the answers offered by Jon and Marc.
Consider this non-generic method:
public static void SetToNull(ref ISomeInterface obj) {
obj = null;
}
Hmm... setting a ref parameter to null. That's only possibly for a reference type, correct? (Well, or for a Nullable<T>; but let's ignore that case to keep things simple.) So the fact that this method compiles tells us that a variable declared to be of some interface type must be treated as a reference type.
The key phrase here is "declared as": consider this attempt to call the above method:
var x = new SomeStruct();
// This line does not compile:
// "Cannot convert from ref SomeStruct to ref ISomeInterface" --
// since x is declared to be of type SomeStruct, it cannot be passed
// to a method that wants a parameter of type ref ISomeInterface.
SetToNull(ref x);
Granted, the reason you can't pass x in the above code to SetToNull is that x would need to be declared as an ISomeInterface for you to be able to pass ref x -- and not because the compiler magically knows that SetToNull includes the line obj = null. But in a way that just reinforces my point: the obj = null line is legal precisely because it would be illegal to pass a variable not declared as an ISomeInterface to the method.
In other words, if a variable is declared as an ISomeInterface, it can be set to null, pure and simple. And that's because interfaces are reference types -- hence, declaring an object as an interface and assigning it to a value type object boxes that value.
Now, on the other hand, consider this hypothetical generic method:
// This method does not compile:
// "Cannot convert null to type parameter 'T' because it could be
// a non-nullable value type. Consider using 'default(T)' instead." --
// since this method could take a variable declared as, e.g., a SomeStruct,
// the compiler cannot assume a null assignment is legal.
public static void SetToNull<T>(ref T obj) where T : ISomeInterface {
obj = null;
}
The MSDN documentation tells us that structs are value, not reference types. They are boxed when converting to/from a variable of type object. But the central question here is: what about a variable of an interface type? Since the interface can also be implemented by a class, then this must be tantamount to converting from a value to a reference type, as Jon Skeet already said, therefore yes boxing would occur. More discussion on an msdn blog.