I have the following integer that I would like to check the value with an if statement.
int myInt = 3;
I tried this code which works, but I don't like having to write the variable name over and over.
if (myInt == 0 || myInt == 2 || myInt == 3 || myInt == 4)
{
Debug.WriteLine("Match");
}
else
{
Debug.WriteLine("No Match");
}
To demonstrate what I would like to ideally have, I tried something like this:
if (myInt == (0 | 2 | 3 | 4))
{
Debug.WriteLine("Match");
}
else
{
Debug.WriteLine("No Match");
}
But this doesn't work because the | is not quite the right operator because there is a type mismatch.
I then tried this, which also worked fine, but I still don't like having to declare an extra array.
if ((new int[] { 0, 2, 3, 4 }).Contains(myInt))
{
Debug.WriteLine("Match");
}
else
{
Debug.WriteLine("No Match");
}
The question is:
Is there an operator that can satisfy what I'm trying to accomplish without declaring an additional array or asking for the same variable name over and over with the || operator?
Your question is
Is there an operator that can satisfy what I'm trying to accomplish without declaring an additional array
but it really should be
Is there an operator that can satisfy what I'm trying to accomplish without declaring an additional array every time
There is nothing wrong with having that array once (and initializing it once), but there is a lot wrong with allocating it (and by extension GCing it later) every single time. So You need to declare an array once, something like
private static int matchArray[] = new int[] { 0, 2, 3, 4 };
and later just
if (matchArray.Contains(myInt)) ...
EDIT
If your match array is small, by any means use the answer by #JohnField and not this one - I stand corrected!
There is possibly a way to achieve what you want to do. They are called enum flags. Here there is a nice answer that explains how they work.
They way you want to use integers remind me of an enum where you have possibly multiple choices. Let's take the following case for instance:
[Flags]
enum DaysOfTheWeek
{
Sunday = 1,
Monday = 2,
Tuesday = 4,
Wednesday = 8,
Thursday = 16,
Friday = 32,
Saturday = 64
}
you can declare a variable as follow:
DaysOfTheWeek daysOfTheWeek;
daysOfTheWeek = DaysOfTheWeek.Monday | DaysOfTheWeek.Wednesday | DaysOfTheWeek.Friday;
and then check if your enum contains one of the values assigned above:
if((daysOfTheWeek & DaysOfTheWeek.Monday) == DaysOfTheWeek.Monday)
{
// Contains Monday!
}
else
{
// DOES NOT Contain Monday!
}
or from .NET 4 onwards:
if(daysOfTheWeek.HasFlag(DaysOfTheWeek.Monday))
{
...
}
Clearly this method is more elegant as long as you need to check for a small number of cases. If you have big array to check against, it would not be a suitable approach.
See, I'm just thinking... why not use a switch?
https://msdn.microsoft.com/en-us/library/06tc147t.aspx
int myInt = 3;
switch (myInt)
{
case 0:
case 2:
case 3:
case 4:
// Match
break;
default:
// No match
break;
}
If your array is in order, Array.BinarySearch will be more efficient than Contains, especially if the array has more than a few elements.
if (Array.BinarySearch(checkArray, myInt) >= 0)
;//match
else
;//no match
Usually, however, the best choice for the Contains operation is the HashSet. Thanks to JohanLarsson for pointing that out.
You could use a switch-case, but it could be argued that this looks messy.
switch (myInt)
{
case 0: //Add a comment to make it clear that
case 2: //this is a deliberate fall-through and
case 3: //not a mistake. (E.g. "//Deliberate
case 4: //fall-through for 0, 2, 3, 4")
//Insert code here in this section to do something
Debug.WriteLine("Match");
break; //Break statement is required to indicate end of section
default:
//The default section runs like an "else" statement
//It is optional and will run if none of the above
//cases are applicable.
Debug.WriteLine("No Match");
break;
} //end switch-case
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I have a method, inside this method, at first, I use switch-case like this:
public int setDeviceName
{
set
{
device_id = value;
DeviceNoList[(page_no - 1) * display_data_num + selectedTagNo - 1] = value;
switch (selectedDeviceNo)
{
case 1:
lblLocation1.Text = lblDeviceName_Selected(device_id);
break;
case 2:
lblLocation2.Text = lblDeviceName_Selected(device_id);
break;
case 3:
lblLocation3.Text = lblDeviceName_Selected(device_id);
break;
case 4:
lblLocation4.Text = lblDeviceName_Selected(device_id);
break;
case 5:
lblLocation5.Text = lblDeviceName_Selected(device_id);
break;
case 6:
lblLocation6.Text = lblDeviceName_Selected(device_id);
break;
case 7:
lblLocation7.Text = lblDeviceName_Selected(device_id);
break;
case 8:
lblLocation8.Text = lblDeviceName_Selected(device_id);
break;
default:
break;
}
}
get
{
return device_id;
}
}
After, I tried to do For loop instead like below:
public int setDeviceName
{
set
{
device_id = value;
DeviceNoList[(page_no - 1) * display_data_num + selectedTagNo - 1] = value;
Label [] lbl_Location = { lblLocation1, lblLocation2, lblLocation3, lblLocation4, lblLocation5, lblLocation6, lblLocation7, lblLocation8 };
//set deviceName to each label in form
for(int i = 0; i<display_data_num; i++)
{
lbl_Location[i].Text = lblDeviceName_Selected(device_id);
}
}
get
{
return device_id;
}
}
I have checked, my program still goes well, I don't know Can I use For instead of Switch-case like that or not. If not, why my program still work. I am new in programming.
A for loop is better than switches and ifs when there are many different cases, all being a number in order. There are many reasons why but the best is: which is easier to read and write? 100 lines of cases or a few lines if a for loop?
In this scenario if you can change it to be in this form, it will be much better. We cannot move it straight over how you wanted, though, because indexing ([i]) doesn't work how you seem to think it does. Indexing grabs the value at the specified index of the collection, maybe an array, a list, etc..
If we change our lbl_Location1, ... to an array:
string[] lbl_Location = new string[]{ "a value 1", "a value 2", "and more" };
Obviously change the strings to the value and type of your choice. You can then do what you want and iterate through the indexes, which is better. Note that indexes start at 0! The first value is 0, second 1, etc..
Implementation (notice I changed your condition in the for statement to the length of the array, this is a cleaner and simpler way to do it. Unless you were doing something different where that wasn't just the length of the array, just stop using that and use this):
for(int i = 0; i<lbl_Location.Length; i++)
{
lbl_Location[i].Text = lblDeviceName_Selected(device_id);
}
If you don't understand something or I misunderstood please comment!
More information on indexing:
So let's say I have a variable called MyString1. What you were trying to do with MyString[1] (I have replaced i with a 1 to further simplify this) is "look for a (for simplicity's sake) array called MyString and look at the second (remember, indexes start at zero!) position." What you wanted it to do was just add a 1 at the end. When we make it an array, we are essentially making one variable that is a container for many values.
How can I test if value of int is for example 1,2,4 or 5? I thought i could do something like this but apparently not.
if(someInt == (1||2||4||5))
Use LINQ:
if ((new[] {1,2,4,5}).Contains(someInt))
Write an extension method
static class MiscExtensions
{
static bool EqualToAny<T>(this T i, params T[] items)
{
return items.Any(x => x.Equals(i));
}
}
And use it like so
static class Program
{
static void Main(string[] args)
{
int myNumber = 5;
if (myNumber.EqualToAny(1, 2, 3, 4, 5))
Console.WriteLine("Hello, World");
}
}
As an alternative you can use:
switch(someInt)
{
case 1:
case 2:
case 4:
case 5:
DoYourStuff();
break;
}
There are two ways I can think of.
Or all the comparisons.
if (someInt == 1 || someInt == 2 || someInt == 4 || someInt == 5) {
}
Or for a more flexible solution see if someInt is in an array.
if (Array.BinarySearch(new[] { 1, 2, 4, 5 }, someInt ) != -1) {
}
You need to write your if statement like this
if (someInt==1 || someInt==2 || someInt==4 || someInt==4)
Or you could use a switch statement
switch (someInt)
{
case 1:
case 2:
case 4:
case 5:
// do something
break;
}
Breaking down your attempted code is quite interesting. You wrote:
if(someInt == (1||2||4||5))
I guess in your head you read it as, if someInt equals 1 or 2 or 4 or 5. And if computers behaved like humans then this would work. But we all know that computers don't behave like that!
The == equality operator, a binary operator, returns true when its two operands are equal. So that means, in your version, if it compiled, you would need someInt to be equal to (1||2||4||5). And for that to even be meaningful, we would need (1||2||4||5) to evaluate to a single value, instead of producing a compile error. And, if it did evaluate to a single value, then it could not have the meaning which you want. Because you want the test to return true when someInt is equal to one of four candidate values.
The bottom line is that == tests for exact equality between precisely two values.
You can't do it like that. Instead use:
if(someInt == 1 || someInt == 2 || someInt == 4 || someInt == 5)
Or also you could use something like this:
if((new List<int>() {1,2,4,5}).Contains(someInt) == true)
Hi before going to direct problem let me show my code :
//Definition of enum
public enum LogType
{
Warning = -2,
Error = -1,
Info = 0,
EruCtorDtor = 1,
Notifications = 2,
CommunicationWithAOT = 4,
ExecutedOrder = 8,
ERUInfo = 16,
DebugLog = 32,
}
//Use of enum
CurrentLogFlagSettings = nLogFlag;
LogFlagMap = new SortedDictionary<LogType, int>();
ulong mask = 1;
while(mask <= nLogFlag)
{
if ((nLogFlag & mask) == mask)
{
LogType type = (LogType)mask; //Step 1
string val = type.ToString(); //Step 2
//Processing the value
LogFlagMap.Add(type, tempVal)
LogMsg(val + " added", type);
}
mask <<= 1;
}
What I want is : Process step2 only after step1 has produced valid value. I mean value should be between range defined in enum definition. Otherwise I dont want to process it.
for e.g.
case 1 - Lets say mask value is 32,
its defined in enum. So type is
getting value DebugLog and so it
type.ToString() (i.e. "DebugLog"),
this is a valid case.
case 2- Lets
say mask value is 128 and its not
defined in enum, in this case I dont
want to process anything on 128
value. But what is happening its
geting value 128 in type and
type.ToString() is converting it
into 128. I dont want this, I want
to make sure whether 128 belongs to
enum values or not.
I want to prevent 2nd case to be executed. Is there any solution for my problem?
Please let me know if more details are needed.
You could use Enum.IsDefined, like so:
int value = 128;
Console.WriteLine(Enum.IsDefined(typeof(LogType), value)); // will print out False
Firstly, let me seriously apologise, Ive had like no sleep, so if I missed the point a little. Please, just ignore me.
You can enumerate your LogType with Enum.GetValues(typeof(LogType))), so you could step through and check a value against it. I had some code, but, I couldnt promise it compiled.
Bool isValid(int i)
{
foreach (LogType l in Enum.GetValues(typeof(LogType)))
{
if ((int)l == i) return true;
}
return false;
}
You can also use Enum.GetValues(typeof(LogType)) to get all the possible values for your enum and do what you want through that.
i.e.
var values = Enum.GetValues(typeof (LogType));
foreach (LogType type in values)
{
if (((int)type & nLogMask) == (int)type)
{
//value is valid, process the value
}
}
One addition to your code could be the addition of the [Flags] attribute to you enum, this then makes it clear that the enum values are for bitwise operations
e.g.
[Flags]
public enum LogType
{
Warning = -2,
Error = -1,
Info = 0,
EruCtorDtor = 1,
Notifications = 2,
CommunicationWithAOT = 4,
ExecutedOrder = 8,
ERUInfo = 16,
DebugLog = 32,
}
although to do this, you would need to change the values such that the Warning and Error take the top 2 bits of the enum value (assuming this is still necessary).
The c# Enum class also has the method GetName(). This might provide a nice and easy manner to retrieve the name of the value set
e.g.
Enum.GetName( typeof(LogType), 4 ); // result = CommunicationWithAOT
I have a library called Unconstrained Melody which allows you to express all of this in a type-safe generic way and avoids boxing too. Personally I prefer that over using Enum.IsDefined, but obviously that doesn't involve learning an extra library.
It's probably not worth using Unconstrained Melody if this is the only thing you need to do with your enum, but if you've got other similar operations, you may wish to consider it.
This is a valid enum
public enum myEnum
{
a= 1,
b= 2,
c= 3,
d= 4,
e= 5,
f= 6,
g= 7,
h= 0xff
};
But this is not
public enum myEnum
{
1a = 1,
2a = 2,
3a = 3,
};
Is there a way I can use an number in a enum? I already have code that would populate dropdowns from enums so it would be quite handy
No identifier at all in C# may begin with a number (for lexical/parsing reasons). Consider adding a [Description] attribute to your enum values:
public enum myEnum
{
[Description("1A")]
OneA = 1,
[Description("2A")]
TwoA = 2,
[Description("3A")]
ThreeA = 3,
};
Then you can get the description from an enum value like this:
((DescriptionAttribute)Attribute.GetCustomAttribute(
typeof(myEnum).GetFields(BindingFlags.Public | BindingFlags.Static)
.Single(x => (myEnum)x.GetValue(null) == enumValue),
typeof(DescriptionAttribute))).Description
Based on XSA's comment below, I wanted to expand on how one could make this more readable. Most simply, you could just create a static (extension) method:
public static string GetDescription(this Enum value)
{
return ((DescriptionAttribute)Attribute.GetCustomAttribute(
value.GetType().GetFields(BindingFlags.Public | BindingFlags.Static)
.Single(x => x.GetValue(null).Equals(value)),
typeof(DescriptionAttribute)))?.Description ?? value.ToString();
}
It's up to you whether you want to make it an extension method, and in the implementation above, I've made it fallback to the enum's normal name if no [DescriptionAttribute] has been provided.
Now you can get the description for an enum value via:
myEnum.OneA.GetDescription()
No, there isn't. C# does not allow identifiers to start with a digit.
Application usability note: In your application you should not display code identifiers to the end-user anyway. Think of translating individual enumeration items into user-friendly displayable texts. Sooner or later you'll have to extend the enum with an item whose identifier won't be in a form displayable to the user.
UPDATE: Note that the way for attaching displayable texts to enumeration items is being discusses, for example, here.
An identifier in C# (and most languages) cannot start with a digit.
If you can modify the code that populates a dropdown with the enumeration names, you could maybe have a hack that strips off a leading underscore when populating the dropdown and define your enum like so:
public enum myEnum
{
_1a = 1,
_2a = 2,
_3a = 3
};
Or if you don't like the underscores you could come up with your own 'prefix-to-be-stripped' scheme (maybe pass the prefix to the constructor or method that will populate the dropdown from the enum).
Short and crisp 4 line code.
We simply use enums as named integer for items in code,
so any simplest way is good to go.
public enum myEnum
{
_1 = 1,
_2,
_3,
};
Also for decimal values,
public enum myEnum
{
_1_5 = 1,
_2_5,
_3_5,
};
So while using this in code,
int i = cmb1.SelectedIndex(0); // not readable
int i = cmb1.SelectedIndex( (int) myEnum._1_5); // readable
No way. A valid identifier (ie a valid enumeration member) cannot start with a digit.
Enumerations are no different than variables in terms of naming rules. Therefore, you can't start the name with a number. From this post, here are the main rules for variable naming.
The name can contain letters, digits, and the underscore character
(_).
The first character of the name must be a letter. The underscore is
also a legal first character, but its
use is not recommended at the
beginning of a name. An underscore is
often used with special commands, and
it's sometimes hard to read.
Case matters (that is, upper- and lowercase letters). C# is
case-sensitive; thus, the names count
and Count refer to two different
variables.
C# keywords can't be used as variable names. Recall that a keyword
is a word that is part of the C#
language. (A complete list of the C#
keywords can be found in Appendix B,
"C# Keywords.")
Identifiers can't start with numbers. However, they can contain numbers.
Here is what i came up with as an alternative, where I needed Enums to use in a "for" Loop and a string representation equivalent to use in a Linq query.
Create enums namespace to be used in "for" Loop.
public enum TrayLevelCodes
{
None,
_5DGS,
_5DG,
_3DGS,
_3DG,
_AADC,
_ADC,
_MAAD,
_MADC
};
Create strings based on enum created to be used for Linq query
public string _5DGS = "\"5DGS\"",
_5DG = "\"5DG\"",
_3DGS = "\"3DGS\"",
_3DG = "\"3DG\"",
_AADC = "\"AADC\"",
_ADC = "\"ADC\"",
_MAAD = "\"MAAD\"",
_MADC = "\"MADC\"";
Create function that will take an enum value as argument and return corresponding string for Linq query.
public string GetCntnrLvlDscptn(TrayLevelCodes enumCode)
{
string sCode = "";
switch (enumCode)
{
case TrayLevelCodes._5DGS:
sCode = "\"5DGS\"";
break;
case TrayLevelCodes._5DG:
sCode = "\"5DG\"";
break;
case TrayLevelCodes._3DGS:
sCode = "\"3DGS\"";
break;
case TrayLevelCodes._3DG:
sCode = "\"3DG\"";
break;
case TrayLevelCodes._AADC:
sCode = "\"AADC\"";
break;
case TrayLevelCodes._ADC:
sCode = "\"AAC\"";
break;
case TrayLevelCodes._MAAD:
sCode = "\"MAAD\"";
break;
case TrayLevelCodes._MADC:
sCode = "\"MADC\"";
break;
default:
sCode = "";
break;
}
return sCode;
}
Here is how i am using what i created above.
for (var trayLevelCode = TrayLevelCodes._5DGS; trayLevelCode <= TrayLevelCodes._MADC; trayLevelCode++)
{
var TrayLvLst = (from i in pair1.Value.AutoMap
where (i.TrayLevelCode == HTMLINFO.GetCntnrLvlDscptn(trayLevelCode))
orderby i.TrayZip, i.GroupZip
group i by i.TrayZip into subTrayLvl
select subTrayLvl).ToList();
foreach (DropShipRecord tray in TrayLvLst)
{
}
}
2009-12-04 UPDATE: For profiling results on a number of the suggestions posted here, see below!
The Question
Consider the following very harmless, very straightforward method, which uses a switch statement to return a defined enum value:
public static MarketDataExchange GetMarketDataExchange(string ActivCode) {
if (ActivCode == null) return MarketDataExchange.NONE;
switch (ActivCode) {
case "": return MarketDataExchange.NBBO;
case "A": return MarketDataExchange.AMEX;
case "B": return MarketDataExchange.BSE;
case "BT": return MarketDataExchange.BATS;
case "C": return MarketDataExchange.NSE;
case "MW": return MarketDataExchange.CHX;
case "N": return MarketDataExchange.NYSE;
case "PA": return MarketDataExchange.ARCA;
case "Q": return MarketDataExchange.NASDAQ;
case "QD": return MarketDataExchange.NASDAQ_ADF;
case "W": return MarketDataExchange.CBOE;
case "X": return MarketDataExchange.PHLX;
case "Y": return MarketDataExchange.DIRECTEDGE;
}
return MarketDataExchange.NONE;
}
My colleague and I batted around a few ideas today about how to actually make this method faster, and we came up with some interesting modifications that did in fact improve its performance rather significantly (proportionally speaking, of course). I'd be interested to know what sorts of optimizations anyone else out there can think up that might not have occurred to us.
Right off the bat, let me just offer a quick disclaimer: this is for fun, and not to fuel the whole "to optimize or not to optimize" debate. That said, if you count yourself among those who dogmatically believe "premature optimization is the root of all evil," just be aware that I work for a high-frequency trading firm, where everything needs to run absolutely as fast as possible--bottleneck or not. So, even though I'm posting this on SO for fun, it isn't just a huge waste of time, either.
One more quick note: I'm interested in two kinds of answers--those that assume every input will be a valid ActivCode (one of the strings in the switch statement above), and those that do not. I am almost certain that making the first assumption allows for further speed improvements; anyway, it did for us. But I know that improvements are possible either way.
The Results
Well, it turns out that the fastest solution so far (that I've tested) came from João Angelo, whose suggestion was actually very simple, yet extremely clever. The solution that my coworker and I had devised (after trying out several approaches, many of which were thought up here as well) came in second place; I was going to post it, but it turns out that Mark Ransom came up with the exact same idea, so just check out his answer!
Since I ran these tests, some other users have posted even newer ideas... I will test them in due time, when I have a few more minutes to spare.
I ran these tests on two different machines: my personal computer at home (a dual-core Athlon with 4 Gb RAM running Windows 7 64-bit) and my development machine at work (a dual-core Athlon with 2 Gb RAM running Windows XP SP3). Obviously, the times were different; however, the relative times, meaning, how each method compared to every other method, were the same. That is to say, the fastest was the fastest on both machines, etc.
Now for the results. (The times I'm posting below are from my home computer.)
But first, for reference--the original switch statement:
1000000 runs: 98.88 ms
Average: 0.09888 microsecond
Fastest optimizations so far:
João Angelo's idea of assigning values to the enums based on the hash codes of the ActivCode strings and then directly casing ActivCode.GetHashCode() to MarketDataExchange:
1000000 runs: 23.64 ms
Average: 0.02364 microsecond
Speed increase: 329.90%
My colleague's and my idea of casting ActivCode[0] to an int and retrieving the appropriate MarketDataExchange from an array initialized on startup (this exact same idea was suggested by Mark Ransom):
1000000 runs: 28.76 ms
Average: 0.02876 microsecond
Speed increase: 253.13%
tster's idea of switching on the output of ActivCode.GetHashCode() instead of ActivCode:
1000000 runs: 34.69 ms
Average: 0.03469 microsecond
Speed increase: 185.04%
The idea, suggested by several users including Auraseer, tster, and kyoryu, of switching on ActivCode[0] instead of ActivCode:
1000000 runs: 36.57 ms
Average: 0.03657 microsecond
Speed increase: 174.66%
Loadmaster's idea of using the fast hash, ActivCode[0] + ActivCode[1]*0x100:
1000000 runs: 39.53 ms
Average: 0.03953 microsecond
Speed increase: 153.53%
Using a hashtable (Dictionary<string, MarketDataExchange>), as suggested by many:
1000000 runs: 88.32 ms
Average: 0.08832 microsecond
Speed increase: 12.36%
Using a binary search:
1000000 runs: 1031 ms
Average: 1.031 microseconds
Speed increase: none (performance worsened)
Let me just say that it has been really cool to see how many different ideas people had on this simple problem. This was very interesting to me, and I'm quite thankful to everyone who has contributed and made a suggestion so far.
Assuming every input will be a valid ActivCode, that you can change the enumeration values and highly coupled to the GetHashCode implementation:
enum MarketDataExchange
{
NONE,
NBBO = 371857150,
AMEX = 372029405,
BSE = 372029408,
BATS = -1850320644,
NSE = 372029407,
CHX = -284236702,
NYSE = 372029412,
ARCA = -734575383,
NASDAQ = 372029421,
NASDAQ_ADF = -1137859911,
CBOE = 372029419,
PHLX = 372029430,
DIRECTEDGE = 372029429
}
public static MarketDataExchange GetMarketDataExchange(string ActivCode)
{
if (ActivCode == null) return MarketDataExchange.NONE;
return (MarketDataExchange)ActivCode.GetHashCode();
}
I'd roll my own fast hash function and use an integer switch statement to avoid string comparisons:
int h = 0;
// Compute fast hash: A[0] + A[1]*0x100
if (ActivCode.Length > 0)
h += (int) ActivCode[0];
if (ActivCode.Length > 1)
h += (int) ActivCode[1] << 8;
// Find a match
switch (h)
{
case 0x0000: return MarketDataExchange.NBBO; // ""
case 0x0041: return MarketDataExchange.AMEX; // "A"
case 0x0042: return MarketDataExchange.BSE; // "B"
case 0x5442: return MarketDataExchange.BATS; // "BT"
case 0x0043: return MarketDataExchange.NSE; // "C"
case 0x574D: return MarketDataExchange.CHX; // "MW"
case 0x004E: return MarketDataExchange.NYSE; // "N"
case 0x4150: return MarketDataExchange.ARCA; // "PA"
case 0x0051: return MarketDataExchange.NASDAQ; // "Q"
case 0x4451: return MarketDataExchange.NASDAQ_ADF; // "QD"
case 0x0057: return MarketDataExchange.CBOE; // "W"
case 0x0058: return MarketDataExchange.PHLX; // "X"
case 0x0059: return MarketDataExchange.DIRECTEDGE; // "Y"
default: return MarketDataExchange.NONE;
}
My tests show that this is about 4.5 times faster than the original code.
If C# had a preprocessor, I'd use a macro to form the case constants.
This technique is faster than using a hash table and certainly faster than using string comparisons. It works for up to four-character strings with 32-bit ints, and up to 8 characters using 64-bit longs.
If you know how often the various codes show up, the more common ones should go at the top of the list, so fewer comparisons are done. But let's assume you don't have that.
Assuming the ActivCode is always valid will of course speed things up. You don't need to test for null or the empty string, and you can leave off one test from the end of the switch. That is, test for everything except Y, and then return DIRECTEDGE if no match is found.
Instead of switching on the whole string, switch on its first letter. For the codes that have more letters, put a second test inside the switch case. Something like this:
switch(ActivCode[0])
{
//etc.
case 'B':
if ( ActivCode.Length == 1 ) return MarketDataExchange.BSE;
else return MarketDataExchange.BATS;
// etc.
}
It would be better if you could go back and change the codes so they are all single characters, because you would then never need more than one test. Better yet would be using the numerical value of the enum, so you can simply cast instead of having to switch/translate in the first place.
I'd use a dictionary for the key value pairs and take advantage of the O(1) lookup time.
Do you have any statistics on which strings are more common? So that those could be checked first?
With a valid input could use
if (ActivCode.Length == 0)
return MarketDataExchange.NBBO;
if (ActivCode.Length == 1)
return (MarketDataExchange) (ActivCode[0]);
return (MarketDataExchange) (ActivCode[0] | ActivCode[1] << 8);
Change the switch to switch on the HashCode() of the strings.
I'd extrapolate tster's reply to "switch over a custom hash function", assuming that the code generator creates a lookup table, or - failing that - building the lookup table myself.
The custom hash function should be simple, e.g.:
(int)ActivCode[0]*2 + ActivCode.Length-1
This would require a table of 51 elements, easily kept in L1 cache, under the following assumptions:
Input data must already be validated
empty string must be handled sepsarately
no two-character-codes start with the same character
adding new cases is hard
the empty string case could be incorporated if you could use an unsafe access to ActivCode[0] yielding the '\0' terminator.
Forgive me if I get something wrong here, I'm extrapolating from my knowledge of C++. For example, if you take ActivCode[0] of an empty string, in C++ you get a character whose value is zero.
Create a two dimensional array which you initialize once; the first dimension is the length of the code, the second is a character value. Populate with the enumeration value you'd like to return. Now your entire function becomes:
public static MarketDataExchange GetMarketDataExchange(string ActivCode) {
return LookupTable[ActivCode.Length][ActivCode[0]];
}
Lucky for you all the two-character codes are unique in the first letter compared to the other two-character codes.
I would put it in dictionary instead of using a switch statement. That being said, it may not make a difference. Or it might. See C# switch statement limitations - why?.
Avoid all string comparisons.
Avoid looking at more than a single character (ever)
Avoid if-else since I want the compiler to be able optimize the best it can
Try to get the result in a single switch jump
code:
public static MarketDataExchange GetMarketDataExchange(string ActivCode) {
if (ActivCode == null) return MarketDataExchange.NONE;
int length = ActivCode.Length;
if (length == 0) return MarketDataExchange.NBBO;
switch (ActivCode[0]) {
case 'A': return MarketDataExchange.AMEX;
case 'B': return (length == 2) ? MarketDataExchange.BATS : MarketDataExchange.BSE;
case 'C': return MarketDataExchange.NSE;
case 'M': return MarketDataExchange.CHX;
case 'N': return MarketDataExchange.NYSE;
case 'P': return MarketDataExchange.ARCA;
case 'Q': return (length == 2) ? MarketDataExchange.NASDAQ_ADF : MarketDataExchange.NASDAQ;
case 'W': return MarketDataExchange.CBOE;
case 'X': return MarketDataExchange.PHLX;
case 'Y': return MarketDataExchange.DIRECTEDGE;
default: return MarketDataExchange.NONE;
}
}
Trade memory for speed by pre-populating an index table to leverage simple pointer arithmetic.
public class Service
{
public static MarketDataExchange GetMarketDataExchange(string ActivCode) {
{
int x = 65, y = 65;
switch(ActivCode.Length)
{
case 1:
x = ActivCode[0];
break;
case 2:
x = ActivCode[0];
y = ActivCode[1];
break;
}
return _table[x, y];
}
static Service()
{
InitTable();
}
public static MarketDataExchange[,] _table =
new MarketDataExchange['Z','Z'];
public static void InitTable()
{
for (int x = 0; x < 'Z'; x++)
for (int y = 0; y < 'Z'; y++)
_table[x, y] = MarketDataExchange.NONE;
SetCell("", MarketDataExchange.NBBO);
SetCell("A", MarketDataExchange.AMEX);
SetCell("B", MarketDataExchange.BSE);
SetCell("BT", MarketDataExchange.BATS);
SetCell("C", MarketDataExchange.NSE);
SetCell("MW", MarketDataExchange.CHX);
SetCell("N", MarketDataExchange.NYSE);
SetCell("PA", MarketDataExchange.ARCA);
SetCell("Q", MarketDataExchange.NASDAQ);
SetCell("QD", MarketDataExchange.NASDAQ_ADF);
SetCell("W", MarketDataExchange.CBOE);
SetCell("X", MarketDataExchange.PHLX);
SetCell("Y", MarketDataExchange.DIRECTEDGE);
}
private static void SetCell(string s, MarketDataExchange exchange)
{
char x = 'A', y = 'A';
switch(s.Length)
{
case 1:
x = s[0];
break;
case 2:
x = s[0];
y = s[1];
break;
}
_table[x, y] = exchange;
}
}
Make the enum byte-based to save a little space.
public enum MarketDataExchange : byte
{
NBBO, AMEX, BSE, BATS, NSE, CHX, NYSE, ARCA,
NASDAQ, NASDAQ_ADF, CBOE, PHLIX, DIRECTEDGE, NONE
}
If the enumeration values are arbitrary you could do this...
public static MarketDataExchange GetValue(string input)
{
switch (input.Length)
{
case 0: return MarketDataExchange.NBBO;
case 1: return (MarketDataExchange)input[0];
case 2: return (MarketDataExchange)(input[0] << 8 | input[1]);
default: return MarketDataExchange.None;
}
}
... if you want to go totally nuts you can also use an unsafe call with pointers as noted by Pavel Minaev ...
The pure cast version above is faster than this unsafe version.
unsafe static MarketDataExchange GetValue(string input)
{
if (input.Length == 1)
return (MarketDataExchange)(input[0]);
fixed (char* buffer = input)
return (MarketDataExchange)(buffer[0] << 8 | buffer[1]);
}
public enum MarketDataExchange
{
NBBO = 0x00, //
AMEX = 0x41, //A
BSE = 0x42, //B
BATS = 0x4254, //BT
NSE = 0x43, //C
CHX = 0x4D57, //MW
NYSE = 0x4E, //N
ARCA = 0x5041, //PA
NASDAQ = 0x51, //Q
NASDAQ_ADF = 0x5144, //QD
CBOE = 0x57, //W
PHLX = 0x58, //X
DIRECTEDGE = 0x59, //Y
None = -1
}
+1 for using a dictionary. Not necessarily for optimization, but it'd be cleaner.
I would probably use constants for the strings as well, though i doubt that'd buy you anything performance wise.
Messy but using a combination of nested ifs and hard coding might just beat the optimiser:-
if (ActivCode < "N") {
// "" to "MW"
if (ActiveCode < "BT") {
// "" to "B"
if (ActiveCode < "B") {
// "" or "A"
if (ActiveCode < "A") {
// must be ""
retrun MarketDataExchange.NBBO;
} else {
// must be "A"
return MarketDataExchange.AMEX;
}
} else {
// must be "B"
return MarketDataExchange.BSE;
}
} else {
// "BT" to "MW"
if (ActiveCode < "MW") {
// "BT" or "C"
if (ActiveCode < "C") {
// must be "BT"
retrun MarketDataExchange.NBBO;
} else {
// must be "C"
return MarketDataExchange.NSE;
}
} else {
// must be "MV"
return MarketDataExchange.CHX;
}
}
} else {
// "N" TO "Y"
if (ActiveCode < "QD") {
// "N" to "Q"
if (ActiveCode < "Q") {
// "N" or "PA"
if (ActiveCode < "PA") {
// must be "N"
retrun MarketDataExchange.NYSE;
} else {
// must be "PA"
return MarketDataExchange.ARCA;
}
} else {
// must be "Q"
return MarketDataExchange.NASDAQ;
}
} else {
// "QD" to "Y"
if (ActiveCode < "X") {
// "QD" or "W"
if (ActiveCode < "W") {
// must be "QD"
retrun MarketDataExchange.NASDAQ_ADF;
} else {
// must be "W"
return MarketDataExchange.CBOE;
}
} else {
// "X" or "Y"
if (ActiveCode < "Y") {
// must be "X"
retrun MarketDataExchange.PHLX;
} else {
// must be "Y"
return MarketDataExchange.DIRECTEDGE;
}
}
}
}
This gets the right function with three or four compares. I wouldnt even think of doing this for real unless your piece of code is expected to run several times a second!
You further otimise it so that only single character compares occurred.
e.g. replace '< "BT" ' with '>= "B" ' -- ever so slightly faster and even less readable!
All your strings are at most 2 chars long, and ASCII, so we can use 1 byte per char.
Furthermore, more likely than not, they also never can have \0 appear in them (.NET string allows for embedded null characters, but many other things don't). With that assumption, we can null-pad all your strings to be exactly 2 bytes each, or an ushort:
"" -> (byte) 0 , (byte) 0 -> (ushort)0x0000
"A" -> (byte)'A', (byte) 0 -> (ushort)0x0041
"B" -> (byte)'B', (byte) 0 -> (ushort)0x0042
"BT" -> (byte)'B', (byte)'T' -> (ushort)0x5442
Now that we have a single integer in a relatively (64K) short range, we can use a lookup table:
MarketDataExchange[] lookup = {
MarketDataExchange.NBBO,
MarketDataExchange.NONE,
MarketDataExchange.NONE,
...
/* at index 0x041 */
MarketDataExchange.AMEX,
MarketDataExchange.BSE,
MarketDataExchange.NSE,
...
};
Now, obtaining the value given a string is:
public static unsafe MarketDataExchange GetMarketDataExchange(string s)
{
// Assume valid input
if (s.Length == 0) return MarketDataExchange.NBBO;
// .NET strings always have '\0' after end of data - abuse that
// to avoid extra checks for 1-char strings. Skip index checks as well.
ushort hash;
fixed (char* data = s)
{
hash = (ushort)data[0] | ((ushort)data[1] << 8);
}
return lookup[hash];
}
Put the cases in a sorted structure with non linear access (like a hash table).
The switch that you have will have a linear time.
You can get a mild speed-up by ordering the codes according to which ones are most used.
But I agree with Cletus: the best speed-up I can think of would be to use a hash map with plenty of room (so that there are no collisions.)
A couple of random thoughts, that may not all be applicable together:
Switch on the first character in the string, rather than the string itself, and do a sub-switch for strings which can contain more than one letter?
A hashtable would certainly guarantee O(1) retrieval, though it might not be faster for smaller numbers of comparisons.
Don't use strings, use enums or something like a flyweight instead. Using strings in this case seems a bit fragile anyway...
And if you really need it to be as fast as possible, why aren't you writing it in assembly? :)
Can we cast the ActivCode to int and then use int in our case statements?
Use the length of the code to create a unique value from that code instead of using GetHashCode() . It turns out there are no collisions if you use the first letter of the code shifted by the length of the code. This reduces the cost to two comparisons, one array index and one shift (on average).
public static MarketDataExchange GetMarketDataExchange(string ActivCode)
{
if (ActivCode == null)
return MarketDataExchange.NONE;
if (ActivCode.Length == 0)
return MarketDataExchange.NBBO;
return (MarketDataExchange)((ActivCode[0] << ActivCode.Length));
}
public enum MarketDataExchange
{
NONE = 0,
NBBO = 1,
AMEX = ('A'<<1),
BSE = ('B'<<1),
BATS = ('B'<<2),
NSE = ('C'<<1),
CHX = ('M'<<2),
NYSE = ('N'<<1),
ARCA = ('P'<<2),
NASDAQ = ('Q'<<1),
NASDAQ_ADF = ('Q'<<2),
CBOE = ('W'<<1),
PHLX = ('X'<<1),
DIRECTEDGE = ('Y'<<1),
}