Why is my enum comparison not finding the proper value? - c#

I am trying to find a specific Enum from a list of objects. Here is the code:
foreach (IEquipment eq in EntityEquipmentList)
{
if (eq.capability == capabilityEnum.Jam)
{
Console.WriteLine(eq.ToString())
}
}
Just to be clear, EntityEquipmentList is a List of IEquipment objects and I am trying to find the one that has "Jam" as it's capability. As you can see in the "if" statement, I want the capability of "Jam".
Enum in question:
Radar = 1
Jam = 2
Radio = 4
LowFreq = 8
HighFreq = 16
And to be clear, I am 100% certain that there is a piece of Equipment in the list with the Capability of Jam.

Note that the values in your capabilities enum are "powers of two" (1, 2, 4, 8, ... instead of 1, 2, 3, 4, ...). This is usually done for flag enums, where an enum value can be a combination of different defined values. For example, an equipment could have the capability of Jammer as well as Radar.
Well, now Jammer + Radar (or, to be precise: Jammer | Radar, using bitwise OR) is not equal to Jammer, which is why your comparison fails. You can fix this by using HasFlag instead of Equals:
if (equipmentCapability.HasFlag(CapabilityEnum.Jammer)) { ... }
In addition, you should add the Flags attribute to your enum. This
documents the fact that these enum values can be combined, and also
causes equipmentCapability.ToString() to output Jammer, Radar instead of the numerical value.

Related

Comparing lists of ints in, while considering two ints not to be equal if repeated

I know this question has been reiterated multiple times, and I think this is straightforward when using lists of reference types. That is also what most questions talk about. When doing this with value types, I can't quite figure it out.
What I want do to is take an incoming request, and ensure that the request has at least the same numbers that are already present. Additional numbers are allowed, and the additional numbers in the request will be persisted. So if [1,2] is already persisted and the request contains [1,2,3], 3 will be persisted. If the request contained only 1 or 2, that would be invalid, and [1,2] would be valid and only the data that already existed would be returned.
The last case mentioned above I've implemented the following way:
Enumerable.SequenceEqual(existing.OrderBy(e => e), request.OrderBy(e => e));
When the lists aren't equal though, I want to treat each int in a list as if it was unique, but I can't figure out how to do this. In the following example, I would like the resulting list to be [1, 4], but it ends up being [4] due to both the numbers 1 in the existing list are excluded due to being equal.
var existing = new List<int> { 1, 1, 2, 3, 4 };
var request = new List<int> { 1, 2, 3 };
existing.Except(request).ToList();
// 4
I have seen that Except accepts a IEqualityComparer, but I stumble when trying to implement this for a value type and I don't know if this is the correct way to go.
Any help on how I should think when approaching this problem would be greatly appreciated. Efficiency is not that important, as the lists will not contain many elements at all.
You could use regular List<T>.Remove(T):
foreach(var e in existing)
{
var actuallyExisted = request.Remove(e);
}
actuallyExisted will be false if it couldn't find e to remove. request will now contain all ints that weren't in existing.

C# Type.GetFields different orders based on environment [duplicate]

This question already has answers here:
if GetFields() doesn't guarantee order, how does LayoutKind.Sequential work
(3 answers)
Closed 2 years ago.
Say you have an enum like the following:
namespace MyApp
{
public enum Colors
{
Red = 0,
Blue = 1,
Green = 2,
Yellow = 3
}
}
You want to retrieve the enum as an array, so you use this function to get all the information and then send it back to the front end.
Type.GetFields(BindingFlags.Public | BindingFlags.Static)
where type is my enum Colors. Now in my local environment, I get the values in the ordered they're declared. Every time I refresh the page the enum array is ordered the same, no changes.
However, my app is already deployed in another environment, and in this server is retrieving the array in different ordered every time. This is causing a front end error but I can't fix something I can't actually see on my local environment.
Does the Type.GetFields function has a different behavior regarding, I don't know, the .NET Framework version I'm using to run the project? That's the only thing I can think of at the moment.
I need to reproduce this in my environments before making this change.
From the Type.GetFields() documentation:
Remarks
The GetFields method does not return fields in a particular order, such as alphabetical or declaration order. Your code must not depend on the order in which fields are returned, because that order varies.
Use Enum.GetNames() instead of Type.GetFields().
The Remarks for that method say:
The elements of the return value array are sorted by the binary values of the enumerated constants (that is, by their unsigned magnitude).
It's specified in the documentation, so it should be consistent.

Checking set of values for combination

So, I am using a group of enums to determine the value of 4 slots. There is one called none, and then 4 called fire,earth,water,air. The values of the slots are held in a dictionary of an integer as the index and the enum as the value. I want to check the values of this dictionary to see if certain combinations are present. I am not sure how to go about doing this most effectively without hard coding every situation. I know how I could store the combinations, but I am not exactly sure how to go about checking that against the values effectively.
The idea is if the combination is fire, fire, fire, fire, then it sets off a certain event/function.
I'm a little confused about the dictionary because it sounds like you're just using the keys to indicate the sequence. I'm guessing this is a SortedDictionary<int, Elements> (although it may not matter.) But first:
To compare any two lists you can use SequenceEquals.
Let's say this is your enum:
enum Elements { None, Fire, Earth, Water, Air }
Let's say the combination you're checking for is:
var elementsToMatch = new Elements[] { Elements.Fire, Elements.Fire, Elements.Fire };
You could do this:
var matches = dictionary.Values.SequenceEquals(elementsToMatch);
It's true if both sets contain the same elements in the same sequence, otherwise false.
If the sequence isn't important, just the combination of elements, then you could sort them first:
var matches = dictionary.Values.OrderBy(v=>v)
.SequenceEquals(elementsToMatch.OrderBy(e=>e));
Is a Dictionary necessary to do this?
You could do this:
var elements = new Dictionary<int, Elements>(); // or SortedDictionary
elements.Add(0, Elements.None);
elements.Add(1, Elements.Fire);
// etc.
Or you could do this:
var elements = new Elements[] { Elements.None, Elements.Water };
Or use a List<Elements>.
In any case the way you access it will be exactly the same:
elements[0]
elements[1]
// etc
With a dictionary it's possible that a key could be missing. There might be an elements[0] and elements[2] but no elements[1].
If the only reason for the Dictionary is to provide a position for each element then you could use an array or a list, because each element can already be referenced by its position.

If statement not working as expected on combined enum value

This is a quirky one.
I have the following code...
foreach (IScanTicket ticket in this) {
if (ticket.Status == TicketStatus.Created || ticket.Status == (TicketStatus.Transfered | TicketStatus.Created))
return ticket;
}
}
When I run this, where the status is Created|Transferred, the if statement seems to fail (not do what it's suppose to).
The interesting thing is that if I debug and step through the code and watch the statement, it always returns TRUE in my debugger, yet it fails to enter the block when I step through the code.
Why would the debugger show that the statement is true, yet continue like it's not? It's like what the debugger is telling me fibs.
Has anyone ever experienced this?
P.S. I'm using Xamarin studio 5.9.7
Too long for a comment:
Actually, the [Flags] attribute does not change an enum's semantics at all, it's most popularly used by the ToString method to emit a series of names rather than a number for a combined value.
Let's say your enum was declared like this (without the Flags attribute):
enum TicketStatus
{
Created = 1,
Transferred = 2,
Sold = 4
}
You could still combine different members and do any arithmetic that applies to a Flags enum:
TicketStatus status = TicketStatus.Created | TicketStatus.Transferred;
However, the following will print 3:
Console.WriteLine(status);
But if you add the [Flags] attribute, it will print Created, Transferred.
Also, it's important to note that by TicketStatus.Created | TicketStatus.Transferred you're really doing a bitwise OR on the underlying integer value, notice how in our example that the assigned values are unambiguously combinable:
Created : 0001
Transferred: 0010
Sold: 0100
Therefore a value of 3 can be unambiguously determined as a combination of Created and Transferred. However if we had this:
enum TicketStatus
{
Created = 1, // 0001
Transferred = 2, // 0010
Sold = 3, // 0011
}
As it is obvious by the binary representations, combining values and checking against members is problematic as combined members could be ambiguous. e.g. what is status here?
status = TicketStatus.Created | TicketStatus.Transferred;
Is it Created, Transferred or is it really Sold? However, the compiler won't complain if you try to do it, which could lead to hard to track down bugs like yours, where some check is not working as you expect it to, so it's on you to ensure the definition is sane for bitwise mixing and comparing.
On a related note, since your if statement is really only checking if the ticket has a Created status, regardless of being combined with other members, here's a better way to check for that (.NET >= 4):
status.HasFlag(TicketStatus.Created)
or (.NET <4):
(status & TicketStatus.Created) != 0
As to why your enum did not work as expected, it is almost certainly because you did not explicitly specify unambigously bitwise combinable values to its members (typically powers of two).
Thanks to #MarcinJuraszek and #YeldarKurmangaliyev.
Seems the [Flags] attribute wasn't set on the enum as I originally thought. Adding this attribute now makes the enum work in either combination.
So it seems that not having this attribute effects the order of joined enum values.

Storing, using custom logic in C#?

I am writing an app where I will give a set of tests/checks to the user through an UI such as:
LayerCount
ActiveLayerType
EffectCount
ActiveEffectType
CurrentTool
HasSelection
HasAlpha
etc
where the user is able to use them to define a custom logic tree such as:
if (LayerCount > 1)
{
if (ActiveLayerType == LayerType.Blend)
{
// #1
}
else if (ActiveEffectType == EffectType.GaussianBlur)
{
// #2
}
}
else if (CurrentTool == Tools.QuickSelect)
{
if (HasSelection)
{
// #3
}
}
Basically I am trying to define some sort of value that will return the Current Level in his custom logic in some way (let's say 1, 2, 3, and so on). Because I need to have the actions of the user interpreted differently based on the Current Level in his logic.
You could basically say that I am trying to hash a set of values based on the Current Level. But the problem is, I don't know how to get the current level to store it as a hash key. If I did, I could write something like:
var currentActionSet = ActionSetTable [GetCurrentLevel()]
and I would get a set of actions the user can perform based on the current level.
Although it would be more desirable to have these levels have fixed values instead of values starting from 1 to n, based on how they are ordered. So (ActiveEffectType == EffectType.GaussianBlur)'s branch would always return a fixed value no matter where it's in the logic tree.
So to sum up, the problems are:
How to define a current level value, preferrably fixed?
If using this current level value as key is the best approach, or if there is a better way of doing this?
Perhaps you can use bitflags and OR them together to make your hashcode?
int hash = ActiveLayerType | ActiveEffectType | CurrentTool
Then at some later point you can AND a perticular flag out again:
if( hash & LayerType.Blend == LayerType.Blend ) { ... } //will be true if the LayerType.Blend has previously been OR:ed into the hash variable
You can OR together enums of diffrent types too if you cast them to ints first, make sure that the enum bit values doesnt clash though.
This approach may or may not be practical for you application, but its something to consider :) more info here: http://msdn.microsoft.com/en-us/library/cc138362.aspx

Categories

Resources