Enum.GetNames() results in unexpected order with negative enum constants - c#

I have the following enum definition (in C#):
public enum ELogLevel
{
General = -1, // Should only be used in drop-down box in Merlinia Administrator log settings
All = 0, // Should not be used as a level, only as a threshold, effectively same as Trace
Trace = 1,
Debug = 2,
Info = 3,
Warn = 4,
Error = 5,
Fatal = 6,
Off = 7 // Should not be used as a level, only as a threshold
}
Now, when I do an Enum.GetNames() on this type I get a string array with 9 elements as expected, but the order is All, Trace, ... , Off, General, which is not what I was expecting.
Here's the MSDN documentation for Enum.GetNames():
"Remarks: The elements of the return value array are sorted by the
values of the enumerated constants."
What's going on here? I can change my program to take this "functionality" into account, but I'd kind of like to know why .NET is doing what it's doing.

This is a known bug with both GetNames() and GetValues() that was reported here, but ended up getting closed as won't fix:
Yes, this method indeed has a bug where it returns the array of enum values sorted as unsigned-types (-2 is 0xFFFFFFFE and -1 is 0xFFFFFFFF in two's complement, that's why they are showing up at the end of the list) instead of returning values sorted by their signed-types.
Unfortunately, we cannot change the sort order of GetValues because we will break all existing .NET programs that have been written to depend on the current sorting behavior [...]
Looks like you'll have to reorder the values yourself.

Depending on how the sorting occurs, it may be that it is sorting the values as if they were unsigned, in which case, -1 = 0xffffffff, which is of course greater than 7.

Related

C# LastIndexOf not giving correct result

in this particular line of code :
correct = Array.LastIndexOf(turns.ToArray(), false, 4, 0);
I get result correct = -1, well how is this even possible ?
turns[0] up to turns[3] are equal to false turns[4]=true and turns[5]=false is it possible to be caused because the last index i want to be looked up to is 4 and it has value different than the required one ?
The issue is with the last argument (count). This restricts the number of elements searched. You are restricting it to search 0 elements starting at index 4. Thus, it doesn't find anything.
Your count indicates searching 0 elements in the section.
correct = Array.LastIndexOf(turns.ToArray(), false, 4, 2);
Try this out:
correct = Array.LastIndexOf(turns.ToArray(), false, turns.Length, turns.Length);
What were you doing wrong:
never hard code array length (especially in your case, when the array is filled with values)
the first index is actually the starting search index from backwards, and the second index is actually the count, i.e. how many items to search (MSDN constructor clarification)
Update 1:
Made a mistake on the starting index and the count number. Updated the changes, thank you #Steve for pointing it out.

What is the best way to add alpha numeric filtering to a C# dataset?

We have a section of code that loads a text file into a data table, and we use the DataTable's select statement to hand it a filter statement and a sort statement:
DataRow[] dataRowArray = table.Select(filterText, sortExp);
This is all loaded as strings since we are pulling it from a file. The issue we are having is that when the user sorts something that is a numeric column, they will get the string sort: 1, 11, 12 .... 2, 20, 21 ... 3, 30, 31 etc. It also impacts the filtering because things like <= 10 will only return 1 and not 2, 3, etc.
What I have done to start is write a custom IComparable type I called AlphaNumericString. Now when the sort runs, it uses that compareTo method and everything works great. But I'm not quite clear on how to get the DataSet filter to play nice with this.
Mostly I am getting errors like:
Cannot perform '=' operation on DataAccess.Source.AlphaNumericString and System.String
I have tried overloading the operands, but they never seem to get used:
public static bool operator <=(AlphaNumericString alpha, object val)
{
// Compare them
}
Could anyone offer suggestions on how I might go about overriding the filter logic?

How to Fix Enum Error in ProtoBuf-Net

I am getting this error when trying to serialize.
The answer to this question:
How to map System Enum's in Protobuf.Net
indicates that this is related to a Flags Enum and that it should be handled in V2. The Enum being reported here is not a Flags Enum:
public enum RunwayDesignator {
NONE = 0,
LEFT = 1,
RIGHT = 2,
CENTER = 3,
WATER = 4,
C = 5,
L = 6,
R = 7,
W = 8,
A = 9,
B = 10,
NOT_APP = 99
}
I assume the '16' refers to something in the Enum although there are not 16 values. I checked also to see if there are any ProtoMember IDs of 16 related to unsages of this enum - there are not. All usages of this enum that are serialized are private fields.
I would appreciate some guidance on how to deal with this.
Mant Thanks
Well - this is embarrassing. The problem is that the value of 16 is indeed being generated. So it looks like this is some sort of programming error on my part. The error message is saying there is no value for 16 in the enum and that is true.
So I can now go back and try and fix my code. Protobuf-Net is nowhere at fault.
I guess this might be useful for others who see this error. Find out where the enum value is being used and see if the code is sending an invalid value. What I don't understand is why I am not seeing some sort of runtime error when trying to set an invalid index for the enum. I need to investigate that now. And here is an answer to that
Why does casting int to invalid enum value NOT throw exception?
It seems that there is no error generated for invalid enum values but protobuf-net does find them

C# Flags parsing

I have wrote enum:
[Flags]
public enum BundleOS {
Win32 = 0,
Win64 = 1,
Mac = 2
}
I need parse it from string, and write to string. Sample of string: "Win32|Win64".
Next code returns invalid result:
BundleOS os;
Boolean result = TryParse<BundleOS>("Win32|Win64", out os);
In result variable I got the false value. But I need true, and os value must to have the BundleOS.Win32|BundleOS.Win64 value.
If I do such operation:
String x = (BundleOS.Win32|BundleOS.Win64).ToString();
I need get such value: "Win32|Win64", but I get "Win64".
Is exists a simple solution of these problems?
Your problem is that your performing a bitwise operation and Win32 equals 0.
So Win64 OR Win32 is actually Win64 OR 0 which returns Win64.
You can set your enum like this:
[Flags]
public enum BundleOS
{
Win32 = 1,
Win64 = 2,
Mac = 4
}
On a side note:
I'll also point out a very good question that was asked earlier this week on how to define flag enums.
In addition to the answer given by #Blachshma regarding your particular flags, if you want to take the string form of "Win32|Win64" and turn it into an actual instance of your enum, you've got a bit more work cut out.
First you'll simply need to split() the string by the '"|"' in order to get the individual values.
Then you can use Enum.GetNames() and Enum.GetValues() to get a list of the names and values for elements in your original enum. You can then loop through the split components, and find the matching entry (and its value) from your original enum.
Try this to parse your flags string. I havn't tested but it should get you started:
BundleOS flags = "Win32|Win64"
.Split('|')
.Select(s => (BundleOS)Enum.Parse(typeof(BundleOs), s))
.Aggregate((f, i) => f|i);
In addition to Blachshma's answer, the MSDN documentation for the FlagsAttribute actually explains very succinctly in the "Guidelines for FlagsAttribute and Enum" section:
Define enumeration constants in powers of two, that is, 1, 2, 4, 8,
and so on. This means the individual flags in combined enumeration
constants do not overlap.
Use None as the name of the flag enumerated constant whose value is
zero. You cannot use the None enumerated constant in a bitwise AND
operation to test for a flag because the result is always zero.
However, you can perform a logical, not a bitwise, comparison between
the numeric value and the None enumerated constant to determine
whether any bits in the numeric value are set.

Need help understanding code

I am taking a C# class and I need help understanding the following code.
The code has an array which represents responses to a survey, with values 1 thru 10.
The output displays these ratings and the frequency of how many times a value was selected.
The following code is from my book, but I have modified it to just a basic example.
int[] responses = { 3, 2, 5, 6, 3, 5 , 4, 5, 5, 5};
int[] frequency = new int[7];
for (int answer = 0; answer < responses.Length; answer++)
++frequency[responses[answer]];
for (int rating = 1; rating < frequency.Length; rating++)
Console.WriteLine(rating + ", " + frequency[rating]);
Console.Read();
How does the line ++frequency[responses[answer]]; work? In looking at this, if I take reponses[answer] the first time through the loop, this would represent responses[0] which would be a 3, correct? This is where I get confused, what does the ++frequency part of this line do?
frequency[responses[answer]] = frequency[responses[answer]] + 1;
EDIT: I think it's pretty unclear to write it like that. As a personal preference, I don't like using unary operations (++x, x++, etc) on elements that have lots of indexes present.
It adds one to the frequency at that location in the array.
For example, the frequency at position 3 (from your example) will be increased by one after that line executes.
EDIT: So, in more detail, when answer = 0, responses[0] = 3, so frequency[3] gets one added to it.
The ++ could very easily be at the end of the command as well. In other words,
++frequency[responses[answer]];
is the same thing (IN THIS CASE) as using
frequency[responses[answer]]++;
Let's break it down: As you point out, on the first pass responses[answer] will evaluate to "3"
So this then looks like ++frequency[3]
The ++ is incrementing the value of the array at index 3 by 1
Simple enough?
I should also point out that applying the ++ before the array rather than after it does effect how the incrementing is executed (although it doesn't effect the results of this code).
For instance:
int n = 2;
int j = ++n;
int k = n++;
What are j and k?
j will be 3, and k will also be 3. This is because if you place the ++ before, it evaluates it first. If you place it at the end, it evaluates it after the rest of the expression.
If it helps, think of ++frequency[] as "frequency = frequency + 1".
If the ++ operator comes before the variable, then the increment is applied before the statement is executed. If the ++ comes afterwards, then the statement is executed and then the variable is incremented.
In this case, it doesn't matter, since incrementing before or after doesn't impact the logic.
Since "responses[answer]" evaluates to a number, that line of code is incrementing the frequency entry at that array index. So the first time through, answer is 0, so responses[answer] is 3, so the frequency[3] box is getting incremented by 1. The next time through, the frequency[2] box is incremented... etc. etc. etc.
frequency is an array, where all elements are initialized to 0 (the default value for an int). The line ++frequency[responses[answer]] will increment the frequency element pointed out by the integer found at responses[answer]. By putting the ++ in front of frequency, the array element will be incremented before the resulting value is returned.
You can read more about the ++ operator here.
In cases like this it's often useful to rewrite the code as you walk it.
When answer = 0
++frequency[responses[0]]
++frequency[3] since responses[0] = 3
frequency now looks like { 0, 0, 0, 1, 0, 0, 0 }
When answer = 1
++frequency[responses[1]]
++frequency[2] since responses[1] = 2
frequency now looks like { 0, 0, 1, 1, 0, 0, 0 }
And so on.
It means increment the value at frequency[ 3 ]. Where 3 is the return result from responses[answer]. Similarly the next iteration would increment the value at frequency[ 2 ].
The ++ operator in C# when applied to an integer will increment it by one.
The specific line you're looking at, frequency is an array of integers with 7 elements. Which is sort of confusing, because the way you explained it in your code, it would appear that this code would break with any value in the responses array above 6.
That issue aside, basically it's incrementing whichever index of the array it's accessing. So in your example responses[0] would be 3. So this line would find the value of frequency[3] and increment it by 1. Since integer arrays are initialized with all values at zero, then after the first iteration, frequency[3] would be 1. Then if there was another 3 later in your responses array, frequency[3] would be incremented again (i.e. responses[4]).
I hope this helps you.
The goal of the code snippet seems to be to determine the number of times each response appears in the 'responses' array. So, for your example set, frequency[3] should be 5, frequency[5] should be 5, etc.
So, the line you are asking about takes the current element from the responses array, and increments the associated value in the frequency array by 1, to indicate that the particular value has been observed in responses.
Once the entire code snippet executes, the frequency array contains the number of times each element from 0 to 7 was observed in the responses array.
It is using the frequency array to count how many times each response was entered. You could have a counter for each answer:
int numberOfOnes = 0;
int numberOfTwos = 0;
// Etc...
But that would be ugly programming and not as easy or efficient. Using the frequency array allows you do not use an if/else if block or a switch and makes your code easier to read.
Another thing about that frequency array.
int[] frequency = new int[7];
This initializes all the integers in the array to 0, that's why you can just start off by incrementing it instead of seeing if it was the first time for that specific response and then initializing it with 1 or something of that nature.
Good luck with all the fun C# you have ahead of you.

Categories