This question already has answers here:
What does the [Flags] Enum Attribute mean in C#?
(14 answers)
Closed 7 years ago.
Say I have an enum defined like so:
[Flags]
public enum LogLevel
{
None = 1,
Pages = 2,
Methods = 4,
Exception =8
}
and a class like:
public static class Log
{
public static LogLevel Level = LogLevel.Methods | LogLevel.Pages;
public static void EnterPage([CallerFilePath]string filePath = "")
{
if (Level == //What value here to check if Level includes Pages?)
{
//Log
}
}
What value do I need to equate Level to to check whether the enum includes Pages?
First of all, flags must have a None = 0 value, because otherwise there's no way to represent a 0 mask (i.e. no value).
Once you've fixed it, you can check if some enumeration value is in some given flag using Enum.HasFlag or the bit-wise & operator:
Level.HasFlag(LogLevel.Pages);
...or:
(Level & LogLevel.Pages) == LogLevel.Pages
Finally, when you implement a flags enumeration, usually enumeration identifier is expressed in plural. In your case, you should refactor LogLevel to LogLevels.
Why &?
Each enumeration value represents a bit in a complete mask. For example, None would be 0, but it could be also represented as 0, 0, 0 where each 0 is one of possible enumeration values in LogLevels. When you provide a mask like LogLevels.Pages | LogLevels.Methods, then the mask is 1, 1, 0.
In order to check if Pages is within the mask, you use a logical AND comparing the mask with one of possible enumeration values:
1, 1, 0 (LogLevels.Pages | LogLevels.Methods)
1, 0, 0 AND (LogLevels.Pages)
--------
1, 0, 0
1 and 1 (true and true == true)
1 and 0 (true and false == false)
0 and 0 (false and false == false).
The whole AND is like isolating the tested enumeration value. If the resulting mask equals the enumeration value, then the mask contains the enumeration value.
Some OP concern
OP said in some comment:
Just a quick question on zero value. here it states that You cannot
use the None enumerated constant in a bitwise AND operation to test
for a flag because the result is always zero. Does that mean if I have
a 0 value I cannot use & and must use HasFlag?
None (i.e. 0) won't be a member of a mask, because 0 doesn't exist. When you produce a mask you're using the OR | logical operator, which is, at the end of the day, an addition.
Think about 1 + 0 == 1. Would you think checking if 0 is within 1 is ever possible?
Actually, the None = 0 value is required to be able to represent and empty mask.
Finally, HasFlag does the AND for you, it's not a magical solution, but a more readable and encapsulated way of performing the so-called AND.
like this
if (Level.HasFlag(LogLevel.Pages))
{
//todo
}
Related
This question already has answers here:
Check inside method whether some optional argument was passed
(10 answers)
Closed 5 years ago.
I want to let a user specify a custom color for a class in the constructor via passing in RGBA bytes. If they do not specify a custom color, a default color from the application settings will be used. If the Alpha is left out, however, assume fully opaque.
What I would like:
public MyClass(byte r_col = -1, byte g_col = -1, byte b_col = -1, byte a_col = 255)
{
if (r_col == -1 | g_col == -1 | b_col == -1)
{
// use default color
}
else
{
this.color = System.Windows.Media.Color.FromArgb(a_col, r_col, g_col, b_col);
}
}
However, there is no "wrong" value for a byte (-1 is invalid), so I am unable to detect if a byte was actually passed into the function. What options do I have? I'd like to avoid function overloading if possible.
Function overloading is much more beautiful in this case:
public MyClass()
{
//Default color
}
public MyClass(byte r_col, byte g_col, byte b_col)
{
color = Color.FromArgb(r_col, g_col, b_col);
}
public MyClass(byte a_col, byte r_col, byte g_col, byte b_col)
{
color = Color.FromArgb(a_col, r_col, g_col, b_col);
}
Of course it is possible to do it without (as Micheal proofed), but it's (P.Kouverakis mentioned) not good API design. Because if you let the user type in parameters, which aren't allowed, this may result in difficult to trace bugs. Never fear more work for a greater result - so in this case, use function overloads.
I suppose this is one of the reasons why C# has nullable types. The following code worked well by using a nullable type to check if usable arguments were passed in.
public MyClass(byte? r_col = null, byte? g_col = null, byte? b_col = null, byte a_col = 255)
{
if (r_col == null | g_col == null | b_col == null)
{
// use default color
}
else
{
System.Windows.Media.Color.FromArgb(a_col,
r_col.GetValueOrDefault(),
g_col.GetValueOrDefault(),
b_col.GetValueOrDefault());
}
}
This is an answer to my own question - other suggestions are also appreciated.
so I'm trying to make a code with &&. However, when I put that in, it said that I couldn't apply the operand to it.
In specific, it reads: Operator "&&" cannot be applied to operands of type 'Server.Enums.PokemonType' and 'Server.Enums.PokemonType'
However, I need to be able to link these two things so I can make the code be two PokemonTypes. So is there any alternative or work-around for not being able to use &&?
If you need it, this is my code:
case 225:
{
//Flying Press -- INCOMPLETE, needs Flying-type
setup.Move.Element = Enums.PokemonType.Fighting && setup.Move.Element = Enums.PokemonType.Flying;
if (setup.Defender.VolatileStatus.GetStatus("Minimize") != null)
{
setup.Multiplier *= 2;
setup.Move.Accuracy = -1;
}
}
break;
The system defined && operator only supports boolean operands. The & operator will work for enums (because it also works on all integer types, which the enums are based on). Of course, if you want an enum that represents the combination of two flag values then you'll want to OR them together (using |), not AND them together.
To add multiple values to a enum variable you need to declare the enum with [Flags] attribute.
So your enum would be:
[Flags]
public enum PokemonType
{
Fighting = 1 << 0,
Flying = 1 << 1,
Normal = 1 << 2,
Dragon = 1 << 3,
}
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
Then assign using Enums.PokemonType.Fighting | Enums.PokemonType.Flying so it is possible to track all values assigned to it later.
This is the enum definition:
[Flags]
enum Animals
{
None = 0,
Dog = 1,
Cat = 2,
Horse = 4,
Zebra = 8,
}
Now, given the following code, why does the HasFlag method return true for the value Animals.None?
Animals myAnimals = Animals.Dog | Animals.Horse;
var hasNone = myAnimals.HasFlag(Animals.None); //true! Why?
var hasCat = myAnimals.HasFlag(Animals.Cat); //false
var hasDog = myAnimals.HasFlag(Animals.Dog); //true
var hasHorse = myAnimals.HasFlag(Animals.Horse); //true
var hasZebra = myAnimals.HasFlag(Animals.Zebra); //false
HasFlag is effectively this:
HasFlag = (GivenFlag & Value) == GivenFlag;
//"Anything" AND 0 == 0 --> always true
I've come up against this before myself.
It's by design in the .NET Framework:
If the underlying value of flag is zero, the method returns true. If this behavior is not desirable, you can use the Equals method to test for equality with zero and call HasFlag only if the underlying value of flag is non-zero, as the following example illustrates.
You can read a little more about this here.
There is already a plethora of answers describing WHY this happens, so I will just add that what you can do to get what you're looking for is to not use HasFlag in that case, but instead do var hasNone = myAnimals == Animals.None.
I personally really loathe extension methods, but it would be possible to put this in an extension on Enum if you really value being able to just write myOptionEnum.HasNoFlags(). I would just run with explicitly checking for the None value in this special case though.
Well Enum.HasFlags resolves as something like the following:
var hasNone = (myAnimals & Animals.None) == Animals.None
This will always be true for a zero-value-enum-field.
From MSDN
The HasFlag method returns the result of the following Boolean expression.
thisInstance And flag = flag
This is just the defined behavior of the HasFlag method. From the MSDN documentation
if the underlying value of flag is zero, the method returns true
I ended up removing the 'None' element. It is a 'magic value' and interferes with proper Flags Enum operations (like HasFlag()).
If there is no value then use Nullable i.e. Animals? (which now supports primitive types)
EDIT: I needed a 'Default' value (that is non-zero) for use in a serializable object in order to avoid using Nullable (which conflicted with the business logic). But I think this is still better than using 'None=0'.
I have the following code:
namespace ConsoleApplication1
{
internal class Program
{
[FlagsAttribute]
private enum RenderType
{
DataUri = 0,
GZip = 1,
ContentPage = 2,
ViewPage = 4,
HomePage = 8
}
private static void Main()
{
// 4.
// Set a new enum in three statements.
RenderType type2 = RenderType.ViewPage;
// 5.
// See if the enum contains this flag.
if ((type2 & RenderType.ViewPage) == RenderType.ViewPage)
{
Console.WriteLine("ViewPage");
}
if ((type2 & RenderType.DataUri) == RenderType.DataUri)
{
Console.WriteLine("DataUri");
}
if ((type2 & RenderType.GZip) == RenderType.GZip)
{
Console.WriteLine("GZip");
}
}
}
}
Whenever I run this code, it gives me an output of:
ViewPage
DataUri
I want output of just ViewPage as I am giving the value to my enum ViewPage.
Can anyone help me here? Why is this? Is there anything wrong with my Enum declaration or code?
You have declared DataUri = 0 so
(type2 & RenderType.DataUri) == RenderType.DataUri
will always evaluate to true.
Start your valid enum values at 1.
Any number bitwise anded with zero is zero. Start the enum with one and increase by powers of two. Also, a better understanding of binary numbers and bitwise operators will help
DataUri is 0: so x & DataUri is always zero!
Try this:
if(type2 != RenderType.DataUri) {
if ((type2 & RenderType.ViewPage) == RenderType.ViewPage)
{
Console.WriteLine("ViewPage");
}
if ((type2 & RenderType.GZip) == RenderType.GZip)
{
Console.WriteLine("GZip");
}
}
When using Bitmasks the value 0 usually indicates a NONE-Flag. So you should start to count from 1 to 2^n, which is a better practise IMHO:
[FlagsAttribute]
private enum RenderType
{
None = 0,
DataUri = 1,
GZip = 2,
ContentPage = 4,
ViewPage = 8,
HomePage = 16
}
Because x & 0 always equals zero.
Don't specify 0 (zero) as a possible value for a [Flags] enum. Remember that a [Flags] enum is a bit field, and a value of zero doesn't really map into any of the fields, so it will always return true.
From the Framework Design Guidelines by Krzysztof Cwalina:
Avoid using flag enum values normal members that are negative or zero.
Negative values produce unexpected/confusing results in bitwise operations. An enum value of zero creates problems with and operations, etc:
The reason is that RenderType.DataUri has the value 0.
What your code does is to inspect the bitwise configuration of the type2 enum variable by performing a bitwise and operation between type2 and the enum member it's testing for.
In your example, type2 has the value 4, which is 0100 in binary (up to the 4 bits that your enum requires). When you test for RenderType.GZip (0001), it does the following calculation:
0100 & 0001 = 0000
Since 0000 != 0001, the RenderType.GZip bit is not set in type2. However, 0100 & 0000 is always 0000, so you'll always get true when checking for RenderType.DataUri.
Essentially, a Flags enum uses a different bit for each of its members, but since 0 doesn't represent a bit in an integer, it won't behave as expected.
As others have said you should start Flags enums at 1 due to how binary arithmetics work. I just want to add that you may want to use the HasFlag method (I believe new in .NET 4.0) to check the flags:
if(type2.HasFlag(RenderType.ViewPage))
{
...
}
http://msdn.microsoft.com/en-us/library/system.enum.hasflag.aspx
Why is it that the following code won't work:
endDate.AddDays(7-endDate.DayOfWeek);
While this will:
endDate.AddDays(0-endDate.DayOfWeek + 7);
?
(By "won't work" I mean results in the following compilation error: "cannot convert from 'System.DayOfWeek' to 'double'")
To expand upon what Lasse said (or rather, make it a little more explicit).
Because 0 is convertable to an Enum type,
0 - endDate.DayOfWeek becomes
(DayOfWeek)0 - endDate.DayOfWeek
And since you can subtract one enum from another and get an integer difference:
(DayOfWeek)0 - endDate.DayOfWeek == (int)endDate.DayOfWeek
Thus, since the result of the subtraction is an int, you can then add 7 to it.
endDate.AddDays(0-endDate.DayOfWeek + 7);
So, if Monday's Enum value is 1
0 - endDate.DayOfWeek == -1 + 7 == 6
However, you can't do the reverse.
endDate.DayOfWeek - 0 + 7,
because the result type of the calculation is dependant upon the leftmost side. Thus, while 0 - endDate.DayOfWeek results in an integer, endDate.DayOfWeek - 0 results in an enum DayOfWeek.
Most interestingly, you could use this side-effect to get the value of an enum without casting, though I would consider this hackish and confusing... thus to be avoided.
int enumValue = -(0 - endDate.DayOfWeek);
This is very interesting. The right way to do this is:
endDate.AddDays(7 - (int)endDate.DayOfWeek);
But, your question isn't about a solution, but a reason for the behavior. It has something to do with the way the compiler treats a zero. Either line fails if no zero is present, while both lines work if a zero is present.
You can subtract two enum values to get their integer value difference:
using System;
namespace ConsoleApplication10
{
public enum X { A, B, C, D }
public class Program
{
static void Main()
{
var x = X.D + X.A;
Console.Out.WriteLine(x);
Console.In.ReadLine();
}
}
}
Will print out 3.
But you can't add, probably makes no sense.
In the case of "0", 0 is auto-convertible to all enum types, so basically "0 - enumvalue" means the same as "(enumtype)0 - enumvalue", which again works.