I have created a permission enum Flags that controls access:
[Flags]
public enum PermissionEnum
{
None = 0,
Read = 1,
Write = 2,
Delete = 4,
All = 7
}
If someone requests access the different enum values will be added to a list:
List<PermissionEnum> permissions = new List<PermissionEnum> { 1, 4 }
How can I flatten out the list of enums to a bit string?
E.g.
[1,4] = "101"
You're doing it the wrong way. Instead of a list, you should store in a single PermissionEnum variable and for each permission to be added, do a bitwise or:
PermissionEnum pe = PermissionEnum.None; //Current value:0
pe |= PermissionEnum.Read; //Add Read permission. Current value: 1
pe |= PermissionEnum.Delete;//Add Delete permission. Current value: 5
Then you can display the value converting the int to a string:
string result = Convert.ToString((int)pe, 2); //yields "101"
To remove a permission, use bitwise and and bitwise not
pe &= ~PermissionEnum.Read; //Removes Read permission. Current value: 4
Also, to check if some permission is setted, use bitwise and and check if is greater than 0:
bool canRead = (pe & PermissionEnum.Read) > 0;
Step 1
Combine your list of flags into a single enum value:
PermissionEnum combined = permissions.Aggregate(PermissionEnum.None,
(cmb, perm) => cmb | perm);
Step 2
Convert the combined value into a bit string:
string bitString = Convert.ToString((int)combined, 2);
Note that if you can change the interface so you just get the combined enum instead of a list, you can avoid step 1 altogether.
Related
I am attempting the below Question, but can not seem to figure out how to extend the enum:
Question:
Each account on a website has a set of access flags that represent a users access.
Update and extend the enum so that it contains three new access flags:
A Writer access flag that is made up of the Submit and Modify flags.
An Editor access flag that is made up of the Delete, Publish and Comment flags.
An Owner access that is made up of the Writer and Editor flags.
For example, the code below should print "False" as the Writer flag does not contain the Delete flag.
Console.WriteLine(Access.Writer.HasFlag(Access.Delete))
using System;
public class Account
{
[Flags]
public enum Access
{
Delete,
Publish,
Submit,
Comment,
Modify
}
public static void Main(string[] args)
{
//Console.WriteLine(Access.Writer.HasFlag(Access.Delete)); //Should print: "False"
}
}
You can do this by giving each flag of your enum a value that, if represented in the binary system, consists of only zeroes and a 1.
[Flags]
public enum Access
{
// Simple flags
Delete = 1, // 00001
Publish = 2, // 00010
Submit = 4, // 00100
Comment = 8, // 01000
Modify = 16, // 10000
// Combined flags
Editor = 11, // 01011
Writer = 20, // 10100
Owner = 31 // 11111
}
This way, writer will have both the submit and modify flag, but not the delete flag.
Why does this work?
The HasFlag method basically does a bitwise AND operation. So when you check whether delete is in the editor flag, it does this. Only if both bits are 1, the resulting bit will also be 1, otherwise 0.
00001
01011
----- &
00001
Check whether delete is in writer:
00001
10100
----- &
00000
If the result is the same as the flag you're passing as a parameter, that means the flag is included!
More literal
You can define the numbers as binary literals as well. That way it is easier to see at a glance what's what.
[Flags]
public enum Access
{
// Simple flags
Delete = 0b00001,
Publish = 0b00010,
Submit = 0b00100,
Comment = 0b01000,
Modify = 0b10000,
// Combined flags
Editor = Delete | Publish | Comment,
Writer = Submit | Modify,
Owner = Editor | Writer
}
or, as I like to write it
[Flags]
public enum Access
{
// Simple flags
Delete = 1,
Publish = 1 << 1,
Submit = 1 << 2,
Comment = 1 << 3,
Modify = 1 << 4,
// Combined flags
Editor = Delete | Publish | Comment,
Writer = Submit | Modify,
Owner = Editor | Writer
}
I have a list of strings in an xml document:
<properties>red yellow blue</properties>
and I have an enum:
[Flags]
public enum Properties
{
None = 0,
red = 1,
yellow = 2,
blue = 4,
green = 8
}
Is there a way to convert the XML string into the enum flag value of 7 or 0111?
There are countless resources around about doing the reverse of this, but I'm having trouble finding any information on converting from a string to Flags.
Yes, but you need them to be comma separated:
[Flags]
public enum Test
{
A = 1,
B = 2,
C = 4
}
Test t;
Enum.TryParse<Test>("A,B", out t);
Since you can't have spaces in the names, you can just do a string replace of space to comma before calling TryParse.
Sure
string flags = "red yellow blue";
var eflags = flags.Split()
.Select(s => (Properties)Enum.Parse(typeof(Properties), s))
.Aggregate((a, e) => a | e);
Console.WriteLine(eflags);
Console.WriteLine((int)eflags);
Outpus
red, yellow, blue
7
I'll leave how to get the string out of the xml up to you.
I have an existing database, and my table has a field in which is stored a value, a sum of all "values" of my checkbox list.
Another programmer made this method, called it "logic sum". I don't know this method, but now I need to know the reverse method, so I can know which checkboxes are selected by using the Sum value of all their values.
I really don't know how to do this ... I am using C# with asp.net, but the logic should to be the same for all languages.
Can you help me?
Normally these things are done with powers of 2... The first checkbox has a "weight" of 1, the second one has a "weight" of 2, the third one a "weight" of 4 and so on.
You can check if a checkbox is checked by doing
if ((sum & 1) != 0) // first is checked
if ((sum & 2) != 0) // second is checked
if ((sum & 4) != 0) // third is checked
or
bool firstIsChecked = (sum & 1) != 0;
bool secondIsChecked = (sum & 2) != 0;
bool thirdIsChecked = (sum & 4) != 0;
and so on.
if you don't like binary math, you can go through an enum to leverage the Enum.HasFlag method:
[Flags]
public enum MyChechboxes
{
FooCheckbox = 1,
BarCheckbox = 2,
BazCheckbox = 4,
}
int sum = 5;
MyChechboxes checkeds = (MyChechboxes)sum;
bool firstIsChecked = checkeds.HasFlag(MyChechboxes.FooCheckbox);
bool secondIsChecked = checkeds.HasFlag(MyChechboxes.BarCheckbox);
bool thirdIsChecked = checkeds.HasFlag(MyChechboxes.BazCheckbox);
Let's assume you have a company with 5 features (A,B,C,D,E)
Saving..
if(A.isChecked)
features = features | 1;
if(B.isChecked)
features = features | 2;
if(C.isChecked)
features = features | 4;
if(D.isChecked)
features = features | 8;
if(E.isChecked)
features = features | 16;
Store features value in you db.
Loading..
Get the value off you db
if(features&1)
A is checked
if(features&2)
B is checked
if(features&4)
C is checked
if(features&8)
D is checked
if(features&16)
E is checked
Do you know how to work with flags?
For this logical sum method to be reversible it must use the same principle as flags enums: each possible combination of checkboxes must result in a single unique number.
I'm guessing there are not that many checkboxes, numbered like this:
1, 2, 4, 8, 16, 32... etc. This way any combination will result in a single number, and the logical sum will be reversible.
if i declare an enum like
enum Weekdays
{
Mon = 1,
Tue = 1,
Wen = 1,
Thi,
Fri,
Sat,
Sun
}
Weekdays obj = (Weekdays)1;
Console.WriteLine(obj);//Prints Tue why?
now if i change Weekdays and do the same operation as follows
enum Weekdays
{
Mon = 1,
Tue = 1,
Wen = 1,
Thi = 1,
Fri,
Sat,
Sun
}
Weekdays obj = (Weekdays)1;
Console.WriteLine(obj);//Prints Thi !!!!!How?
What is really happening here?
When you write out your value like this, it ends up getting ToString() called on it.
Console.WriteLine(obj);
If you dig into the code far enough down, it's calling Enum.GetName() on your value.
Regarding multiple enum values with the same underlying value, the Enum.GetName page on MSDN says:
If multiple enumeration members have the same underlying value, the GetName method guarantees that it will return the name of one of those enumeration members. However, it does not guarantee that it will always return the name of the same enumeration member. As a result, when multiple enumeration members have the same value, your application code should never depend on the method returning a particular member's name.
It doesn't state how it determines which name to return if the values on two or more are the same.
The docs for Enum.ToString() include the same warning, in slightly different wording.
Digging a little deeper, the method above makes a call to Array.BinarySearch, passing it an array of numbers representing all values in your enum, and a number representing the value you want to print.
So you have an array with multiple 1's in it, and you're searching for a 1. The docs for that call are similar:
Duplicate elements are allowed. If the Array contains more than one element equal to value, the method returns the index of only one of the occurrences, and not necessarily the first one.
Again, it doesn't state how a selection is made, just that it'll be unreliable.
When you assign similar values, the result will be unexpected but I think it will evaluate for two cases:
When n is even:
(n/2)
When n is odd:
(n/2)+1
If I change the enum like this:
enum Weekdays {Mon=1,Tue=1,Wen=1,Thi=1,Fri=1,Sat=1, Sun=1, Mon2=1, Mon3=1}
// n is odd = 9
// (n/2)+1 = 5
Weekdays obj = (Weekdays)1;
Console.WriteLine(obj);
The result will be Fri, Now lets change the enum again:
enum Weekdays {Mon=1,Tue=1,Wen=1,Thi=1,Fri=1,Sat=1, Sun=1,Mon2=1}
// n is even = 8
// (n/2) = 4
Weekdays obj = (Weekdays)1;
Console.WriteLine(obj);
The result is now Thi, Again change the enum:
enum Weekdays {Mon=1,Tue=1,Wen=1,Thi=1,Fri=1,Sat=1, Sun=1}
// n is odd = 7
// (n/2)+1 = 4
Weekdays obj = (Weekdays)1;
Console.WriteLine(obj);
The result is now Thi, Again change the enum:
enum Weekdays {Mon=1,Tue=1,Wen=1,Thi=1,Fri=1,Sat=1}
// n is even = 6
// (n/2) = 3
Weekdays obj = (Weekdays)1;
Console.WriteLine(obj);
The result is now Wen, Again change the enum:
enum Weekdays {Mon=1,Tue=1,Wen=1,Thi=1,Fri=1}
// n is odd = 5
// (n/2)+1 = 3
Weekdays obj = (Weekdays)1;
Console.WriteLine(obj);
The result is now Wen, Changing the enum again:
enum Weekdays {Mon=1,Tue=1,Wen=1,Thi=1}
// n is even = 4
// (n/2) = 2
Weekdays obj = (Weekdays)1;
Console.WriteLine(obj);
The result is now Tue, Changing the enum again:
enum Weekdays {Mon=1,Tue=1,Wen=1}
// n is odd = 3
// (n/2)+1 = 2
Weekdays obj = (Weekdays)1;
Console.WriteLine(obj);
The result is now Tue.
Even though this is explaining the behavior perfectly but this may not always happen or may not happen since I have not checked this for more cases but as MSDN says you should not assume about such output when the enum have same values for different names...
That said, I think you can easily understand now what is happening in your code.
Ref.: Link
Edit:
#GrantWinney's answer led me to this, He has written that Array.BinarySearch is passed the array of values and the value to search for so I realized from the name Array.BinarySearch that it is definitely using a BinarySearch and that explains everything...
Binary Search will divide the array like this:
Mid = {Low(which is the starting index) + High (which is the last index of array)}/2
and then Check for
if (Mid == value) return index;
else
if the value is smaller or equal move left other wise move right of the array
So this explains it how the enum values are printed if their are multiple names for the value you are trying to print.
Your Original Question
enum Weekdays
{
Mon = 1,
Tue = 1,
Wen = 1,
Thi,
Fri,
Sat,
Sun
}
Weekdays obj = (Weekdays)1;
Console.WriteLine(obj);//Prints Tue why?
It prints Tue because a call to Array.BinarySearch will be made passing the array
{1, 1, 1, 2, 3, 4, 5}
and a value to search which is 1...
So the BinarySearch will do this:
Mid = {Low(0) + High(6)} / 2
if (Mid == value) return index
else move left
After moving left again the Mid will be calculated:
High = Mid - 1; // now only the left sub-array will be searched
Mid = {Low(0) + High(2)} / 2
if (Mid == value) return index // here the condition will be true and you will be returned with `Tue`
The 2nd example in your Question:
enum Weekdays
{
Mon = 1,
Tue = 1,
Wen = 1,
Thi = 1,
Fri,
Sat,
Sun
}
Weekdays obj = (Weekdays)1;
Console.WriteLine(obj);//Prints Thi !!!!!How?
As I have written above a call to Array.BinarySearch will be made and array:
{1, 1, 1, 1, 2, 3, 4}
will be passed with value = 1 to search...
Apply the BinarySearch algorithm on the array and it will evaluate to Thi.
As Per MSDN If multiple enumeration members have the same underlying value and you attempt to retrieve the string representation of an enumeration member's name based on its underlying value, your code should not make any assumptions about which name the method will return.
I was trying to add multiple enum values to a variable based on conditions. For example, I have something like this:
SharedAccessBlobPermissions t = SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.Read;
if user has chosen other available enum values on the UI, i want to add them was well. But it looks like we can only write it on one line and can't add multiple values later on.
Any idea how can I achieve this?
[Update]
Based on the answers, this is what I wrote
var t= new SharedAccessBlobPermissions();
if (isAllowRead)
{
t = SharedAccessBlobPermissions.Read;
}
if (isAllowWrite)
{
t |= SharedAccessBlobPermissions.Write;
}
If SharedAccessBlobPermissions has been declared with [Flags] attribute
you can do some set arithemtics. If initially
SharedAccessBlobPermissions t = SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.Read;
Addtion:
// Add SharedAccessBlobPermissions.Delete and SharedAccessBlobPermissions.Clear
t |= SharedAccessBlobPermissions.Delete | SharedAccessBlobPermissions.Clear;
Subtraction:
// Remove SharedAccessBlobPermissions.Delete
t = (t | SharedAccessBlobPermissions.Delete) ^ SharedAccessBlobPermissions.Delete;
You can keep adding more values to the bitmask by bitwise-oring them with the current value:
t |= SharedAccessBlobPermissions.AnotherOption
You need to use Flags attribute.
See the following example:
[Flags]
enum DaysOfWeek
{
Sunday = 1,
Monday = 2,
Tuesday = 4,
Wednesday = 8,
Thursday = 16,
Friday = 32,
Saturday = 64
}
public void RunOnDays(DaysOfWeek days)
{
bool isTuesdaySet = (days & DaysOfWeek.Tuesday) == DaysOfWeek.Tuesday;
if (isTuesdaySet)
//...
// Do your work here..
}
public void CallMethodWithTuesdayAndThursday()
{
this.RunOnDays(DaysOfWeek.Tuesday | DaysOfWeek.Thursday);
}
I suggest you to read the following Thread
Here is another useful answer