patterncsharpMinor
Generic method for obtaining enum items in an ushort object
Viewed 0 times
genericobtainingmethodenumushortitemsforobject
Problem
The idea is to perform a bit-wise scan of an
The code I developed is as follows:
As you can see, I use a generic type to define the return type.
I searched the site and found that constraining the generic type with the following
The code generates a list of the enum provided to contains the matches found.
Then it proceeds to scan the input value to find any and all items that masked (a bit-wise operation as stated before) do match.
If a match is found it adds the enum case to the return list.
I want to know whether this is a good or bad implementation and ways to make it better, it performs as expected but I feel like there's room for optimization.
To provide an example, suppose the following enum and input:
The output for this code would be a List of type SampleCases with 2 items, Case2 and Case 3 since both (bit-wise) are contained in the sample provided. Tell me if this works as an example for you.
ushort provided to find any and all matches within an enum collection.The code I developed is as follows:
public List GetEnumItemsFromUshort(ushort input) where T : struct, IComparable, IConvertible, IFormattable
{
var output = new List();
foreach (T enumValue in Enum.GetValues(typeof(T)))
{
if ((input & enumValue.ToUInt16(new CultureInfo("en-US"))) == enumValue.ToUInt16(new CultureInfo("en-US")))
{
output.Add(enumValue);
}
}
return output;
}As you can see, I use a generic type to define the return type.
I searched the site and found that constraining the generic type with the following
struct, IComparable, IConvertible, IFormattable, makes it behave like an enum.The code generates a list of the enum provided to contains the matches found.
Then it proceeds to scan the input value to find any and all items that masked (a bit-wise operation as stated before) do match.
If a match is found it adds the enum case to the return list.
I want to know whether this is a good or bad implementation and ways to make it better, it performs as expected but I feel like there's room for optimization.
To provide an example, suppose the following enum and input:
public ushort sample = 6;
public enum SampleCases
{
Case1 = 0x1,
Case2 = 0x2,
Case3 = 0x4,
Case4 = 0x8,
}The output for this code would be a List of type SampleCases with 2 items, Case2 and Case 3 since both (bit-wise) are contained in the sample provided. Tell me if this works as an example for you.
Solution
Code can be greatly simplified but first I'd list few things:
1) If your enum is a bitmap then you should mark it with
2) you shouldn't convert an
3) You shouldn't create a new instance of
3) To know if a value is valid you can use
To put all together:
It's a minor change and a micro-optimization but I'd also change return type to
Note that 0 is not handled, you may want to include it or not; I'd leave it out because it's always matched unless you have
Inverting point of view you may write:
This 2nd version will also handle shared bits like
Note the hacky
Very last point: your constraints for generic parameter reduce wrong usage of your function but it doesn't really limit usage to enums: most primitive types implement same interfaces. I'd also add an explicit check:
1) If your enum is a bitmap then you should mark it with
[Flags].[Flags]
public enum SampleCases
{
Case1 = 0x1,
Case2 = 0x2,
Case3 = 0x4,
Case4 = 0x8,
}2) you shouldn't convert an
enum value to ushort, enums default type is int and for [Flags] you may (easily?) run out of bits. If value is out of ushort range (your input) simply drop it, Convert.ToInt16() will throw OverflowException for out of range values.3) You shouldn't create a new instance of
CultureInfo every time you need it. It may be expansive. Moreover you're using en-US culture, you already have the CultureInfo.InvariantCulture.3) To know if a value is valid you can use
Enum.IsDefined().To put all together:
public List GetEnumItemsFromUshort(ushort input)
where T : struct, IComparable, IConvertible, IFormattable
{
const int bitsInUInt16 = sizeof(ushort) * 8;
return Enumerable.Range(0, bitsInUInt16 - 1)
.Select(x => 1 (input & x) == x && Enum.IsDefined(typeof(T), x))
.Select(x => (T)Convert.ChangeType(x, typeof(T)))
.ToList();
}It's a minor change and a micro-optimization but I'd also change return type to
IEnumerable. If caller will need a list then it will be able to call ToList() itself and if it does not need it then you will avoid an unnecessary copy. Also first Select() may be dropped and embedded in Where() clause.Note that 0 is not handled, you may want to include it or not; I'd leave it out because it's always matched unless you have
input == 0. Note that to do not have a 0 default value for an enum is a dangerous practice, value types are 0 initialized and an uninitialized enum will have an unknown value.Inverting point of view you may write:
public List GetEnumItemsFromUshort(ushort input)
where T : struct, IComparable, IConvertible, IFormattable
{
return Enum.GetValues(typeof(T))
.Cast()
.Where(x => ((ushort)x & input) == (ushort)x)
.Cast()
.ToList();
}This 2nd version will also handle shared bits like
GuestDefault, UserDefault and AdminDefault in this example:[Flags]
enum SampleCases {
None = 0,
Read = 1,
Write = 2,
Execute = 4,
GuestDefault = Read,
UserDefault = Read | Execute,
AdminDefault = Read | Write | Execute
}Note the hacky
.Cast() to use .Where() over a non-generic enumeration (also paying boxing price). Here I tried to keep code short but I wouldn't implement this with LINQ (generated enumerator with yield return will be more clear, IMO). Also this code returns duplicates and aliases, if it's not what you want then you have to handle them (checking for distinct values and for multiple bits set on the same value).Very last point: your constraints for generic parameter reduce wrong usage of your function but it doesn't really limit usage to enums: most primitive types implement same interfaces. I'd also add an explicit check:
if (!typeof(T).IsEnum))
throw new ArgumentException("Template argument must be an enum type.");Code Snippets
[Flags]
public enum SampleCases
{
Case1 = 0x1,
Case2 = 0x2,
Case3 = 0x4,
Case4 = 0x8,
}public List<T> GetEnumItemsFromUshort<T>(ushort input)
where T : struct, IComparable, IConvertible, IFormattable
{
const int bitsInUInt16 = sizeof(ushort) * 8;
return Enumerable.Range(0, bitsInUInt16 - 1)
.Select(x => 1 << x)
.Where(x => (input & x) == x && Enum.IsDefined(typeof(T), x))
.Select(x => (T)Convert.ChangeType(x, typeof(T)))
.ToList();
}public List<T> GetEnumItemsFromUshort<T>(ushort input)
where T : struct, IComparable, IConvertible, IFormattable
{
return Enum.GetValues(typeof(T))
.Cast<object>()
.Where(x => ((ushort)x & input) == (ushort)x)
.Cast<T>()
.ToList();
}[Flags]
enum SampleCases {
None = 0,
Read = 1,
Write = 2,
Execute = 4,
GuestDefault = Read,
UserDefault = Read | Execute,
AdminDefault = Read | Write | Execute
}if (!typeof(T).IsEnum))
throw new ArgumentException("Template argument must be an enum type.");Context
StackExchange Code Review Q#111011, answer score: 7
Revisions (0)
No revisions yet.