In C#, can you use number ranges in enum types, for example
public enum BookType
{
Novel = 1,
Journal = 2,
Reference = 3,
TextBook = 4 .. 10
}
EDIT: The reason this is needed is to cast from a number to the enum type, eg:
int iBook = 5
BookType btBook = (BookType)ibook
Debug.Print "Book " + ibook + " is a " btBook
and the expected output is: Book 5 is a TextBook
As others have said, no it isn't possible. It is possible to combine enum values if they are flags:
[Flags]
public enum BookType
{
Novel = 0,
Journal = 1 << 0,
Reference = 1 << 1,
TextBook1 = 1 << 2,
TextBook2 = 1 << 3,
TextBook3 = 1 << 4,
TextBook4 = 1 << 5,
TextBook5 = 1 << 6,
TextBooks1To5 = TextBook1 | TextBook2 | TextBook3 | TextBook4 | TextBook5
}
According to the C# standard (p612, The C# Programming Language) the value given to an enumeration must be a constant integer (or any similar type - long, byte, sbyte, short, etc), so a range of values isn't valid.
My compiler (VS2008) agrees with the spec.
Since you can't repeat names within an enumeration, the closest you'll get is something like this:
public enum BookType
{
Novel = 1,
Journal = 2,
Reference = 3,
TextBook4 = 4,
TextBook5 = 5, ...
TextBook10 = 10
}
Which is actually pretty ugly. Perhaps an enum is not the solution to your particular problem ...
Since you are unable to assign a value range to an enum, I thought of an alternative approach in order to assign an enum as a limit value (the example is based on time):
private enum eTiming
{
eOnTime = 0,
eSlightDelay = 5,
eMinorDelay = 15,
eDelayed = 30,
eMajorDelay = 45,
eSevereDelay = 60
}
private static eTiming CheckIfTimeDelayed(TimeSpan tsTime)
{
eTiming etiming = eTiming.eOnTime;
foreach (eTiming eT in Enum.GetValues(typeof(eTiming)))
{
if (Convert.ToInt16(eT) <= tsTime.TotalMinutes)
etiming = eT;
}
return etiming;
}
This is assuming that the enum is sorted, and works unexpectedly with signed (-) values.
Somewhat offtopic, you can simulate enums using normal classes with readonly fields.
e.g. something similair to this would solve your problem:
public sealed class BookType
{
public static readonly BookType Novel = new BookType(1, 1, "Novel");
public static readonly BookType Journal = new BookType(2, 2, "Journal");
public static readonly BookType Reference = new BookType(3, 3, "Reference");
public static readonly BookType Textbook = new BookType(4, 10, "Textbook");
public int Low { get; private set; }
public int High { get; private set; }
private string name;
private static class BookTypeLookup
{
public static readonly Dictionary<int, BookType> lookup = new Dictionary<int, BookType>();
}
private BookType(int low, int high, string name)
{
this.Low = low;
this.High = high;
this.name = name;
for (int i = low; i <= high; i++)
BookTypeLookup.lookup.Add(i, this);
}
public override string ToString()
{
return name;
}
public static implicit operator BookType(int value)
{
BookType result = null;
if (BookTypeLookup.lookup.TryGetValue(value, out result))
return result;
throw new ArgumentOutOfRangeException("BookType not found");
}
}
It's quite a bit more verbose than a normal enum, but it does allow you to define ranged members in an enum like manner.
e.g.
var bookType = (BookType)5;
Console.WriteLine(bookType);
If you can assign the values to enum-strings yourself then you can use some bitmagic to map multiple int values to same enum value. Subtypes could be enums themselves for every BookType (NovelTypes, JournalTypes, etc).
On the downside
it does require some value modification when casting to BookType
every subtype range is of the same size (16 in current example.
it is a bit less readable than simple Novel = 3 kind of mapping.
Example code:
class Program
{
/// <summary> Number of subtypes reserved for each BookType. </summary>
private const byte BookTypeStep = 16;
/// <summary> Bitmask to use to extract BookType from a byte. </summary>
private const byte BookTypeExtractor = Byte.MaxValue - BookTypeStep + 1;
/// <summary> Bitmask to use to extract Book subtype from a byte. </summary>
private const byte BookSubTypeExtractor = BookTypeStep -1;
public enum BookType : byte
{
Unknown = 0,
Novel = BookTypeStep * 1,
Journal = BookTypeStep * 2,
Reference = BookTypeStep * 3,
TextBook = BookTypeStep * 4,
}
static void Main(string[] args)
{
for(int i = 16; i < 80; i++)
{
Console.WriteLine("{0}\tof type {1} ({2}),\tsubtype nr {3}",
i,
i & BookTypeExtractor,
(BookType)(i & BookTypeExtractor),
i & BookSubTypeExtractor
);
}
Console.ReadLine();
}
}
This example has ranges 16-31 for Novels, 32-47 for journals, etc.
No, it's not. If the numeric constants you're trying to map to truly have the same meaning, you would still need a separate member for each numeric constant. Like TextBook4, TextBook5, etc.
You can opt for a dictionary rather.
var BookType = new Dictionary<int, string>();
BookType.Add(1, "Novel");
BookType.Add(2, "Journal");
BookType.Add(3, "Reference");
BookType.Add(4, "TextBook");
BookType.Add(5, "TextBook");
BookType.Add(6, "TextBook");
BookType.Add(7, "TextBook");
BookType.Add(8, "TextBook");
BookType.Add(9, "TextBook");
BookType.Add(10, "TextBook");
int iBook = 5
Debug.Print "Book " + iBook + " is a " BookType[iBook]
Edit: You can also declare your dictionary readonly if it's in class level.
You can use an enum as the Dictionary value instead of string.
Perhaps you meant to ask a related question, which is: can you have more than one enumeration (not sure I have the terminology correct, but my example will show what I mean) for each value?
In C#, you can do something like this:
public enum BookType
{
Novel = 1,
Journal = 2,
Magazine = 2,
Reference = 3,
TextBook = 4,
LabWorkbook = 4,
Dictionary = 5,
Encyclopedia = 5
}
This way, you can use either BookType.Journal or BookType.Magazine in your code, either of which is synonymous with the value 2. Whether this should be done is another matter - I'm not familiar with the arguments for or against this (I'd like to say "if C# allows it, it must be OK", but that would be utterly crazy).
The simplest answer to your question is No. The easiest way to accomplish what you desire is:
public enum BookType
{
Novel = 1,
Journal = 2,
Reference = 3,
TextBook = 4
}
public void bookOutput(int book)
{
if(book < 4)
Console.Writeline("Book "+book+" is a " + ((BookType)book).ToString());
else
Console.Writeline("Book "+book+" is a " + BookType.TextBook.ToString());
}
The idea is to let enum be individual values and handle the range with logic statements.
Related
Regarding a question I got from one of my friends I want to ask the best possible solution
The situation is that I have a list of integers for example
2 5 6 8
And I want to get to the integers 17
I can only use each integers ones.
The closest you can get in this case is 16 because no combination leads up to 17.
public class Item
{
public int Weight { get; set; }
public int Value { get; set; }
}
public class Program
{
public static void Main()
{
var items = new[]
{
new Item {Value = 60, Weight = 10},
new Item {Value = 100, Weight = 20},
new Item {Value = 120, Weight = 30},
};
Console.WriteLine(KnapSackRecursive(items, 50));
}
public static int KnapSackRecursive(Item[] items, int capacity)
{
// keep track of the best value seen.
//TODO: Make it a list of numbers
int best = 0;
for (int i = 0; i < items.Length; i++)
{
// This is an array of the other items.
var otherItems = items.Take(i).Concat(items.Skip(i + 1)).ToArray();
// Calculate the best value without using the current item.
int without = KnapSackRecursive(otherItems, capacity);
int with = 0;
// If the current item fits then calculate the best value for
// a capacity less it's weight and with it removed from contention
// and add the current items value to that.
if (items[i].Weight <= capacity)
{
with = KnapSackRecursive(otherItems, capacity - items[i].Weight)
+ items[i].Value;
}
// The current best is the max of the with or without.
int currentBest = Math.Max(without, with);
// determine if the current best is the overall best.
if (currentBest > best)
best = currentBest;
}
return best;
}
}
Edit: It now finds the best possible weight based on the list. It'll result in finding that 20+30 = 50 so it returns 100+120 = 220 I want it to return ("Found best possible combination: 100 + 120 = 220") not just ("220")
I have enum flags. How to get all combinations of flags that do not contain a specific one?
[Flags]
public enum TestEnum
{
A = 1,
B = 2,
C = 4,
D = 8
}
public static IEnumerable<Enum> AllNotContaining(this Enum value)
{....}
For example, TestEnum.A.AllNotContaining() should return {2, 4, 6, 8, 10, 12, 14}.
Step 1, use the binary NOT:
var notUsedBits = ~ value;
But this will set all of the 32 bits that were not used.
So you will probably want a mask:
[Flags]
public enum TestEnum
{
A = 1,
B = 2,
C = 4,
D = 8,
All = A|B|C|D, // or compute this inside the method
}
and then the method becomes
// untested
public static TestEnum AllNotContaining(this TestEnum value)
{
return ~ value & TestEnum.All;
}
this does not return an IEnumerable but that is weird (and inefficient) for a Flags enum anyway.
I haven't tried to polish the code below, but you should get the general idea:
public static IEnumerable<int> AllNotContaining<T>(this T value)
// where T : Enum (as of C# 7.3).
{
// Determine upper bound of values to check.
// E.g. for your test enum, the maximum value is 8 so we need to check up to 15.
var values = Enum.GetValues(typeof(T)).Cast<int>();
int max = values.Max() * 2 - 1;
// Test all values to see if the given flag is present. If not, return it.
for(int i = 0; i <= max; ++i)
{
// Possibly also: if( ((Enum)i).HasFlags(value))
if((max & Convert.ToInt32(value)) == 0)
{
yield return i;
}
}
}
Try like this:
public static IEnumerable<TestEnum> AllNotContaining(this TestEnum value)
{
return Enum.GetValues(typeof(TestEnum)).Cast<TestEnum>().Where(x => x != value).AsEnumerable();
}
The task is to write a simple method that can sort int array (in ascending or descending order - should be set as enum type parameter of this method). I have written the method itself and enum, but I have no idea how to set enum as method parameter:(
Would be great to get any help from you, guys, cause I am completely new to coding.
class Program
{
public enum options
{
UpSortOption,
DownSortOption
}
public static void Main(string[] args)
{
int[] arr = new int[] { 3, 8, 0, 2, 16 };
}
static void orderArray(int [] array, options op)
{
switch(op)
{
case options.UpSortOption:
Array.Sort(array);
foreach (int number in array)
{
Console.Write(number + " ");
}
break;
case options.DownSortOption:
Array.Sort(array);
Array.Reverse(array);
foreach (int number in array)
{
Console.Write(number + " ");
}
break;
}
}
}
The signature of the method looks fine, Now you wanted to call this method by passing the first parameter of type integer array and the second parameter of type options for that you can use the following code:
orderArray(arr,options.UpSortOption);
Or else you can declare a variable of type options and pass that variable, the change you have to make for that case will be:
options optionsVariable = options.DownSortOption;
orderArray(arr,optionsVariable);
Let's take a step back to see if it helps your understanding.
If you have a method that takes a string and an int like this
string MyMethod(string str, int num)
{
// Do something
}
You'd use it like this
string rslt = MyMethod("Hello", 123);
What you've got here is something that takes in some stuff, does something to it, and gives you something in return. In this case MyMethod takes a string and an int, does something with them and returns a string which you then call rslt.
Your example follows the same basic pattern so you need to take your method - orderArray and give it the two things it wants - an int array and an option like this
int[] arr = new int[] { 3, 8, 0, 2, 16 };
orderArray(arr, options.UpSortOption);
Alternatively, you could create an options much like you'd create a string and then call your method like this
int[] arr = new int[] { 3, 8, 0, 2, 16 };
options myOption = options.UpSortOption;
orderArray(arr, myOption);
To fully illustrate the point that an enum as a parameter isn't any different from say a string you could modify your method like this
static void orderArray(int[] array, string op)
{
if (op == "UpSortOption")
{
Array.Sort(array);
foreach (int number in array)
{
Console.Write(number + " ");
}
}
else
{
Array.Sort(array);
Array.Reverse(array);
foreach (int number in array)
{
Console.Write(number + " ");
}
}
}
Then call it like this
int[] arr = new int[] { 3, 8, 0, 2, 16 };
string myOption = "UpSortOption";
orderArray(arr, myOption);
This is how you pass it as a parameter
orderArray(int [] array, typeof(options)){
//Beep bap boop
}
Hope this aids you.
Exactly that's how you set up Enum as a method parameter.
To call this method use:
orderArray(arr, options.UpSortOption);
You can also assign enum value to a variable and use this to call a method:
options sortOpt = options.UpSortOption;
orderArray(arr, sortOpt);
You can also cast integer to an enum:
orderArray(arr, (options)0);
OR
int opt = 0;
orderArray(arr, (options)opt);
Remembering, that if not specified otherwise, first element is 0, second is 1 and so on.
By the way, you should rather use PascalCase to name an enum type, like:
public enum Options
{ ... }
I have a variable representing a quantity in some given unit:
enum Unit
{
Single,
Thousand,
Million,
Billion,
Trillion
}
public class Quantity()
{
public double number;
public Unit numberUnit;
public Int64 GetNumberInSingleUnits()
{
// ???
}
}
For example, imagine
var GDP_Of_America = new Quantiy { number = 16.66, numberUnit = Unit.Trillion };
Int64 gdp = GDP_Of_America.GetNumberinSingleUnits(); // should return 16,660,000,000,000
My question is basically - how can I implement the "GetNumberInSingleUnits" function?
I can't just multiply with some UInt64 factor, e.g.
double num = 0.5;
UInt64 factor = 1000000000000;
var result = num * factor; // won't work! results in double
As the regular numeric operations reslt in a double, but the result may be larger than the range of valid doubles.
How could I do this conversion?
ps, I know the class "Quantity" is not a great way to store information - but this is bound by the input data of my application, which is in non-single (e.g. millions, billions etc) units.
Like I said, decimals can help you here:
public enum Unit
{
Micro = -6, Milli = -3, Centi = -2, Deci = -1,
One /* Don't really like this name */, Deca, Hecto, Kilo, Mega = 6, Giga = 9
}
public struct Quantity
{
public decimal Value { get; private set; }
public Unit Unit { get; private set; }
public Quantity(decimal value, Unit unit) :
this()
{
Value = value;
Unit = unit;
}
public decimal OneValue /* This one either */
{
get
{
return Value * (decimal)Math.Pow(10, (int)Unit);
}
}
}
With decimals you won't lose a lot of precision until after you decide to convert them to long (and beware of over/underflows).
Anton's answer seems like a good solution.
Just for the sake of discussion, another potential way.
I don't like this one at all as it seems very messy; However I think this might avoid imprecisions, if these ever turned out to be an issue with decimals.
public Int64 GetAsInt64(double number)
{
// Returns 1 for single units, 3 for thousands, 6 for millions, etc.
uint e = GetFactorExponent();
// Converts to scientific notation
// e.g. number = -1.2345, unit millions to "-1.2345e6"
string str = String.Format("{0:#,0.###########################}", number) + "e" + e;
// Parses scientific notation into Int64
Int64 result = Int64.Parse(str, NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent | NumberStyles.AllowThousands);
return result;
}
I would like to choose the third element from an enum containing three elements while knowing which two I have already chosen. What is the most effcient way of comparing enums?
EDIT:
So far I have come up with the following:
Drawable.Row alternateChoice = (Drawable.Row)ExtensionMethods.Extensions.DefaultChoice(new List<int>() { (int)chosenEnum1, (int)chosenEnum2 }, new List<int>() { 0, 1, 2 });
Drawable.Row is the enum, the first list is what has already been chosen, and the second list contains the possible choices. The definition of DefaultChoice follows. I am aware it has quadratic time-complexity which is why I'm asking for a better solution:
public static int DefaultChoice(List<int> chosen, List<int> choices)
{
bool found = false;
foreach (int choice in choices)
{
foreach (int chosenInt in chosen)
{
if (chosenInt == choice)
{
found = true;
break;
}
}
if (!found)
{
return choice;
}
found = false;
}
return -1;
}
Try this:
List<MyEnum> selectedValues = new List<MyEnum>();
selectedValues.Add(MyEnum.firstValue); // Add selected value
selectedValues.Add(MyEnum.secondValue); // Add selected value
List<MyEnum> valuesLeftOver = Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>().ToList();
valuesLeftOver = valuesLeftOver.Except(selectedValues).ToList<MyEnum>(); // This will result in the remaining items (third item) being in valuesLeftOver list.
Short and simple code.
Try not to worry about efficiency too much. Optimization algorithms are so powerful these days that you will probably get same assembly code doing this rather than trying to do it manually.
[Flags]
public enum NumberEnum : byte
{
None = 0,
One = 1,
Two = 2,
Three = 4
};
public string GetRemainingEnumItem(NumberEnum filterFlags = 0)
{
if (((filterFlags & NumberEnum.One) == NumberEnum.One) && ((filterFlags & NumberEnum.Two) == NumberEnum.Two))
{
//1 & 2 are selected so item 3 is what you want
}
if (((filterFlags & NumberEnum.One) == NumberEnum.One) && ((filterFlags & NumberEnum.Three) == NumberEnum.Three))
{
//1 & 3 are selected so item 2 is what you want
}
if (((filterFlags & NumberEnum.Three) == NumberEnum.Three) && ((filterFlags & NumberEnum.Two) == NumberEnum.Two))
{
//2 & 3 are selected so item 1 is what you want
}
}
This is how you call it:
var testVal = NumberEnum.One | NumberEnum.Two;
var resultWillEqual3 = GetRemainingEnumItem(testVal);
You might like to try this approach...
public enum Marx {chico, groucho, harpo};
public Marx OtherOne(Marx x, Marx y)
{
return (Marx)((int)Marx.chico + (int)Marx.groucho + (int)Marx.harpo - (int)x - (int)y);
} // OtherOne
// ...
Marx a = Marx.harpo;
Marx b = Marx.chico;
Marx other = OtherOne(a, b); // picks groucho
This may be of use if you are using an enum marked as [Flags]...
public class Enums2
{
[Flags] public enum Bits {none = 0, aBit = 1, bBit = 2, cBit = 4, dBit = 8};
public static readonly Bits allBits;
static Enums2()
{
allBits = Bits.none;
foreach (Bits b in Enum.GetValues(typeof(Bits)))
{
allBits |= b;
}
} // static ctor
public static Bits OtherBits(Bits x)
// Returns all the Bits not on in x
{
return x ^ allBits;
} // OtherBits
// ...
Bits someBits = Bits.aBit | Bits.dBit;
Bits missingBits = OtherBits(someBits); // gives bBit and cBit
} // Enums2