Non Static/Constant values in case statements within a Switch - c#

I'd like to do something like this in c sharp:
int i = 0;
foreach ( Item item in _Items )
{
foreach (Field theField in doc.Form.Fields)
{
switch (theField.Name)
{
case "Num" + i++.ToString(): // Number of Packages
theField.Value = string.Empty;
break;
}
}
}
I have 20 or so fields named Num1, Num2, etc. If I can do this all in one statement/block, I'd prefer to do so.
But the compiler complains that the case statements need to be constant values. Is there a way to use dynamic variables in the case statement so I can avoid repeating code?
I just thought I'd mention, the purpose of this method is to populate the fields in PDF form, with naming conventions which I can not control. There are 20 rows of fields, with names like "Num1" - "Num20". This is why string concatenation would be helpful in my scenario.

No. This is simply part of the language. If the values aren't constants, you'll have to use if/else if or a similar solution. (If we knew more details about what you were trying to achieve, we may be able to give more details of the solution.)
Fundamentally, I'd question a design which has a naming convention for the fields like this - it sounds like really you should have a collection to start with, which is considerably easier to work with.

Yes case value must be able to be evaluated at compile time. How about this instead
foreach (Field theField in doc.Form.Fields)
{
if(theField.Name == ("Num" + i++))
{
theField.Value = string.Empty;
}
}

How about:
int i = 0;
foreach ( Item item in _Items )
doc.Form.Fields.First(f=>f.Name == "Num" + i++.ToString()).Value = string.Empty;
Not sure what the purpose of item is in your code though.

No. There is no way.
How about replacing the switch with:
if (theField.Name.StartsWith("Num"))
theField.Value = string.Empty;
or some similar test?

var matchingFields =
from item in _Items
join field in doc.Form.Fields
on "Num" + item.PackageCount equals field.Name
select field;
foreach (var field in matchingFields)
{
field.Value = string.Empty;
}
For more efficiency include a DistinctBy on field Name after getting the matching fields (from MoreLINQ or equivalent).

Also consider that every time you concatenate two or more strings together the compiler will create memory variables for each of the component strings and then another one for the final string. This is memory intensive and for very large strings like error reporting it can even be a performance problem, and can also lead to memory fragmentation within your programs running, for long-running programs. Perhaps not so much in this case, but it is good to develop those best-practices into your usual development routines.
So instead of writing like this:
"Num" + i++.ToString()
Consider writing like this:
string.Format("{0}{1}", "Num", i++.ToString());
Also you may want to consider putting strings like "Num" into a separate constants class. Having string constants in your code can lead to program rigidity, and limit program flexibility over time as your program grows.
So you might have something like this at the beginning of your program:
using SysConst = MyNamespace.Constants.SystemConstants;
Then your code would look like this:
string.Format("{0}{1}", SysConst.Num, i++.ToString());
And in your SystemConstants class, you'd have something like this:
/// <summary>System Constants</summary>
public static class SystemConstants
{
/// <summary>The Num string.</summary>
public static readonly string Num = #"Num";
}
That way if you need to use the "Num" string any place else in your program, then you can just use the 'SysConst.Num'
Furthermore, any time you decide to change "Num" to "Number", say perhaps per a customer request, then you only need to change it in one place, and not a big find-replace in your system.

Related

Is there a better (faster, more convenient) way to concatenate multiple (nullable) objects?

I've ended up writing my own helper-class to concatenate objects: ConcatHelper.cs.
You see some examples in the gist, but also in the following snippet:
model.Summary = new ConcatHelper(", ")
.Concat(diploma.CodeProfession /* can be any object, will be null checked and ToString() called */)
.BraceStart() // if everything between the braces is null or empty, the braces will not appear
.Concat(diploma.CodeDiplomaType)
.Concat(label: DiplomaMessage.SrkRegisterId, labelSeparator: " ", valueDecorator: string.Empty, valueToAdd: diploma.SrkRegisterId)
.BraceEnd()
.Concat(diploma.CodeCountry)
.BraceStart()
.Concat(diploma.DateOfIssue?.Year.ToString(CultureInfo.InvariantCulture)) // no separator will be added if concatenated string is null or empty (no ", ,")
.BraceEnd()
.Concat(DiplomaMessage.Recognition, " ", string.Empty, diploma.DateOfRecognition?.Year.ToString(CultureInfo.InvariantCulture))
.ToString(); // results in something like: Drogist / Drogistin (Eidgenössischer Abschluss, SRK-Registrierungsnummer 3099-239), Irland (1991)
Benefits:
Does the null checks for you, avoids if/else branches.
Supports labeling, decorating and delimiting values. Doesn't add a label if the value will be null.
Joins everything, fluent notation - less codes
Good to do summaries of domain-objects.
Contra:
Rather slow:
I measured 7ms for the above example
I measured 0.01026ms per concatenation in a real-life example (see unit-test gist)
It's not static (could it be?)
Needs a list to keep track of everything.
Probably an overkill.
So as I am now starting to override a lot of ToString() methods of domain objects, I am unsure, if there is a better way.
By better I basically mean:
Is there a library that already does the stuff I need?
If not, can it be speed up without losing the convenient fluent-notation?
So I would be happy if you show me either a convenient way to achieve the same result without my helper, or helping me improving this class.
Greetings,
flo
Update:
Look at this gist for a real-life UnitTest.
I do not see any real problem with your code. But I would prefer a more streamlined syntax. It may look like this in the end:
string result = ConcatHelper.Concat(
diploma.CodeProfession,
new Brace(
diploma.CodeDiplomaType,
new LabeledItem(label: DiplomaMessage.SrkRegisterId, labelSeparator: " ",
valueDecorator: string.Empty, valueToAdd: diploma.SrkRegisterId)
),
diploma.CodeCountry,
new Brace(
diploma.DateOfIssue?.Year.ToString(CultureInfo.InvariantCulture)
),
DiplomaMessage.Recognition
).ToString();
no wall of Text
you do not have to repeat Concat over and over again
now chance to mix up the braces
Concat() would be of the type static ConcatHelper Concat(objs object[] params) in this case. Brace and LabeledItem need to be handled by ConcatHelper of course (if (obj is LabeledItem) { ... }).
Regarding your contras:
It should be fast enough (10us/ call should be okay). If you really need it faster, you probably should use a single String.Format()
Concat can be static. Just create the ConcatHelper-object inside the Concat-call.
Yes, it needs a list. Is there a problem?
It may be overkill, it may not. If you use this type of code regularly, Utility classes can save you much time and make the code more readable.
Variant 1 - stream like
var sb = new StringBuilder();
const string delimiter = ", ";
var first = true;
Action<object> append = _ => {
if(null!=_){
if(!first){ sb.Append(delimiter);}
first = false;
sb.Append(_.ToString());
}
}
append(diploma.X);
append(diploma.Y);
...
Another one - with collection
var data = new List<object>();
data.Add(diploma.X);
data.Add(diploma.Y);
...
var result = string.Join(", ",data.Where(_=>null!=_).Select(_=>_.ToString()));
It's not much efficient but it allow you addition step between data preparation and joining to do somwthing over collection itself.

How to make your if shorter?

I understood that when using Regex I can enter the value | to search for many values. For example:
Regex sabrina = new Regex("Rihanna|rihanna|Sabrina|sabrina");
I have a string which I want to compare to other values, so I use if like this:
if (rihanna == "Rihanna" || rihanna == "sabrina")
My question is if it possible to make the if shorter? I know this code is not working but what I'm looking for something like this:
if (rihanna == "Rihanna|sabrina")
Single line solution:
if("Rihanna|rihanna|Sabrina|sabrina".Split(new[] {'|'}).Contains(rihanna))
or without String.Split method usage:
if ((new[] { "Rihanna", "rihanna", "Sabrina", "sabrina" }).Contains(rihanna))
If that kind of check is performed more than once you should save string[] array of possible values into a variable:
var names = new[] { "Rihanna", "rihanna", "Sabrina", "sabrina" };
if(names.Contains(rihanna))
You could use a List<string> along with the Enumerable.Contains method.
Example:
var values = new List<string> { "Rihanna", "rihanna", "Sabrina", "sabrina" };
if (values.Contains(rihanna))
{
// etc.
}
The if statement is now a lot more concise, and you can add more items to your list quickly and easily. If you're performing this operation frequently, you should move the declaration of the list to a member variable in the containing class so that you don't incur the overhead of list initialization every time this method is called.
An even better solution would be to use Enumerable.Any and String.Equals to perform a case-insensitive comparison:
if (values.Any(x => String.Equals(rihanna, x, StringComparison.OrdinalIgnoreCase))
{
// your code here
}
Although the if statement is now longer, the solution is much more robust.
If you're looking for easier maintenance of a potentially long list of string candidates, why not use a switch statement? Technically it will be "longer" character-wise, but if you have to modify things down the road, or provide some subtle secondary processing, it could make things a lot easier.
As an alternative to the above answers, perhaps you could consider using an extension method:
public static bool EqualsAny(this string input, params string[] values)
{
return values.Any(v => String.Equals(input, v, StringComparison.OrdinalIgnoreCase));
}
With usage like:
if (rihanna.EqualsAny("Rihanna", "Sabrina"))
This should give pretty readable/compact/short if comparisons for you, especially in comparison to some of the other alternatives here.
Tweak the EqualsAny check if you want to have the OrdinalIgnoreCase or not or feel free to add any overloads you wish. Could even implement the whole piping/splitting where you'd pass in "Rihanna|Sabrina".
EDIT: Perhaps more critically, it centralizes how you compare rather than duplicating the code everywhere.
You can use your own Regex and use it as a single line..
if (Regex.Match(rihanna, "Rihanna|rihanna|Sabrina|sabrina").Success)
Or make it case-insensative like this..
if (Regex.Match(rihanna, "rihanna|sabrina", RegexOptions.IgnoreCase).Success){
Ignore casing, which looks like something you're going for, and use String.Compare:
if(String.Compare(rihanna, "sabrina", StringComparison.OrdinalIgnoreCase) == 0 ||
String.Compare(rihanna, "rihana", StringComparison.OrdinalIgnoreCase) == 0 ) {
// do stuff
}
Dictionary or HashSet (potentially with case insensitive check on names) is another approach which will give you better performance if you have more than several items and repeat checks often.
var listToCheck = new Dictionary<string, string>(
StringComparer.CurrentCultureIgnoreCase) { { "rihanna", null}};
if (listToCheck.ContainsKey("Rihanna")) ....

Handling null in Linq

I am new to linq, and this keeps popping on a null volume field. The file is unpredictable, and it will happen so I would like to put a 0 in where there is an exception. any quick and easy way to do it?
var qry =
from line in File.ReadAllLines("C:\\temp\\T.txt")
let myRecX = line.Split(',')
select new myRec()
{
price = Convert.ToDecimal( myRecX[0].Replace("price = ", "")) ,
volume = Convert.ToInt32(myRecX[1].Replace("volume =", "")),
dTime = Convert.ToDateTime( myRecX[2].Replace("timestamp =", ""))
};
If you would like to use a default when the incoming data is null, empty, or consists entirely of whitespace characters, you can do it like this:
volume = string.IsNullOrWhitesplace(myRecX[1])
? defaultVolume // <<== You can use any constant here
: Convert.ToInt32(myRecX[1].Replace("volume =", ""))
However, this is a "quick and dirty" way of achieving what you need, because the position of each named parameter remains hardcoded. A more robust way would be writing a mini-parser that pays attention to the names of attributes specified in the file, rather than replacing them with an empty string.
You could use something like this, which offers an expressive way to write what you want:
static TOutput Convert<TInput, TOutput>(
TInput value,
params Func<TInput, TOutput>[] options)
{
foreach (var option in options) {
try { return option(value); }
catch { }
}
throw new InvalidOperationException("No option succeeded.");
}
Used like:
select new myRec()
{
price = Convert(myRecX[0].Replace("price = ", ""),
input => Convert.ToDecimal(input),
or => 0M),
...
};
The function indirection and implicit array construction may incur a slight performance penalty, but it gives you a nice syntax with which to specify a number of possible conversions, where the first successful one is taken.
I think here there's an issue beyond the use of Linq.
In general is bad practice manipulating file data before sanitizing it.
Ever if the following question is on the filename (rather than it's content) is a good starting point to understand the concept of sanitizing input:
C# Sanitize File Name
After all yourself tells that your code lacks control of the file content, so before call:
let myRecX = line.Split(',')
I suggest define a private method like:
string SanitizeInputLine(string input) {
// here do whatever is needed to bring back input to
// a valid format in a way that subsequent calls will not
// fail
return input;
}
Applying it is straightforward;
let myRecX = SanitizeInputLine(line).Split(',')
As general rule never trust input.
Let me quote Chapter 10 named _All Input Is Evil!__ of Writing Secure Code by Howard/LeBlanc:
...you should never trust data until data is validated. Failure to do
so will render your application vulnerable. Or, put another way: all
input is evil until proven otherwise.

Best approach to programming highly complex business/math rules

I have to take a piece of data, and apply a large number of possible variables to it. I really don't like the idea of using a gigantic set of if statements, so i'm looking for help in an approach to simplify, and make it easier to maintain.
As an example:
if (isSoccer)
val = soccerBaseVal;
else if (isFootball)
val = footballBaseVal;
.... // 20 different sports
if (isMale)
val += 1;
else
val += 5;
switch(dayOfWeek)
{
case DayOfWeek.Monday:
val += 12;
...
}
etc.. etc.. etc.. with possibly in the range of 100-200 different tests and formula variations.
This just seems like a maintenance nightmare. Any suggestions?
EDIT:
To further add to the problem, many variables are only used in certain situations, so it's more than just a fixed set of logic with different values. The logic itself has to change based on conditions, possibly conditions applied from previous variables (if val > threshold, for instance).
So yes, i agree about using lookups for many of the values, but I also have to have variable logic.
A common way to avoid large switching structures is to put the information into data structures. Create an enumeration SportType and a Dictionary<SportType, Int32> containing the associated values. The you can simply write val += sportTypeScoreMap[sportType] and you are done.
Variations of this pattern will help you in many similar situations.
public enum SportType
{
Soccer, Football, ...
}
public sealed class Foo
{
private static readonly IDictionary<SportType, Int32> sportTypeScoreMap =
new Dictionary<SportType, Int32>
{
{ Soccer, 30 },
{ Football, 20 },
...
}
private static readonly IDictionary<DayOfWeek, Int32> dayOfWeekScoreMap =
new Dictionary<DayOfWeek, Int32>
{
{ DayOfWeek.Monday, 12 },
{ DayOfWeek.Tuesday, 20 },
...
}
public Int32 GetScore(SportType sportType, DayOfWeek dayOfWeek)
{
return Foo.sportTypeScoreMap[sportType]
+ Foo.dayOfWeekScoreMap[dayOfWeek];
}
}
Use either a switch statement or filter function.
By filter function, I mean something like:
func filter(var object, var value)
{
if(object == value)
object = valueDictionary['value'];
}
Then apply the filter with:
filter(theObject, soccer)
filter(theObject, football)
Note that the filter works much better using a dictionary, but it is not required.
Cribbing from The Pragmatic Programmer, you could use a DSL to encapsulate the rules and write a process engine. For your presented problem, a solution might look like:
MATCH{
Soccer soccerBaseVal
IsMale 5
!IsMale 1
}
SWITCH{
Monday 12
Tuesday 13
}
Then match everything in the first col of MATCH, and the first item in each SWITCH you come to. You can make whatever syntax you feel like, then just write a bit of script to cram that into code (or use Xtext because it looks pretty cool).
Here are a few ideas:
1 Use lookup tables:
var val = 0;
SportType sportType = GetSportType();
val += sportvalues[sportType];
You can load the table from the database.
2 Use the factory pattern:
var val = 0;
val += SportFactory.Create(sportType).CalculateValue();
The Dynamic Factory Pattern is useful in situations were new (sport) types are added frequently to the code. This pattern uses reflection to prevent the factory class (or any global configuration) from being changed. It allows you to simply add a new class to your code.
Of course the use of an dynamic factory, or even a factory can be overkill in your situation. You're the only one who can tell.
As a first step I would probably break up each logical processing area into its own method: (May not be the best names as a first pass)
EnforceSportRules
ProcessSportDetails
EnforceGenderRules
Next, depending on how complex the rules are, I may break each section into its own class and have them processed by a main class (like a factory).
GenderRules
GenderContext
I have nothing special to offer you than to first recommend not to just leave it as a big block-- break it into sections, make comment dividers between important parts.
Another suggestion is if you are going to have many very short tests as in your example, break from convention and put the val incrementors on the same line as the evaluatation and indent so they align with eachother.
if (isSoccer) val = soccerBaseVal;
if (isMale) val += 1;
else val += 5;
switch(dayOfWeek){
case DayOfWeek.Monday: val += 12;
...
}
Excess whitespace can make those hundred things into several hundred lines, making vertical scrolling excessive and difficult to get an overall view of the thing.
If you are really just adding values in this sort, I would either create an enumeration with defined indices that correspond to stored values in an array. Then you can do something like this:
enum Sport
{
football = 0,
soccer = 1,
//...
}
int sportValues[] = {
/* footballValue */,
/* soccerValue */,
/* ...Values */
};
int ApplyRules(Sport sport, /* other params */)
{
int value = startingValue;
value += sportValues[(int)sport];
value += /* other rules in same fashion */;
}
Consider implementing the Strategy Pattern which utilizes inheritance/polymorphism to make managing individual functions sane. By seperating each function into its own dedicated class you can forego the nightmare of having miles-long case blocks or if statements.
Not sure if C# supports it yet (or ever will) but VB.NET integrates XML Comment CompletionList directives into intellisense, which--when combined with the Strategy Pattern--can give you the ease of use of an Enum with the open-ended extensibility of OO.

c# performance: type comparison vs. string comparison

Which is faster? This:
bool isEqual = (MyObject1 is MyObject2)
Or this:
bool isEqual = ("blah" == "blah1")
It would be helpful to figure out which one is faster. Obviously, if you apply .ToUpper() to each side of the string comparison like programmers often do, that would require reallocating memory which affects performance. But how about if .ToUpper() is out of the equation like in the above sample?
I'm a little confused here.
As other answers have noted, you're comparing apples and oranges. ::rimshot::
If you want to determine if an object is of a certain type use the is operator.
If you want to compare strings use the == operator (or other appropriate comparison method if you need something fancy like case-insensitive comparisons).
How fast one operation is compared to the other (no pun intended) doesn't seem to really matter.
After closer reading, I think that you want to compare the speed of string comparisions with the speed of reference comparisons (the type of comparison used in the System.Object base type).
If that's the case, then the answer is that reference comparisons will never be slower than any other string comparison. Reference comparison in .NET is pretty much analogous to comparing pointers in C - about as fast as you can get.
However, how would you feel if a string variable s had the value "I'm a string", but the following comparison failed:
if (((object) s) == ((object) "I'm a string")) { ... }
If you simply compared references, that might happen depending on how the value of s was created. If it ended up not being interned, it would not have the same reference as the literal string, so the comparison would fail. So you might have a faster comparison that didn't always work. That seems to be a bad optimization.
According to the book Maximizing .NET Performance
the call
bool isEqual = String.Equals("test", "test");
is identical in performance to
bool isEqual = ("test" == "test");
The call
bool isEqual = "test".Equals("test");
is theoretically slower than the call to the static String.Equals method, but I think you'll need to compare several million strings in order to actually detect a speed difference.
My tip to you is this; don't worry about which string comparison method is slower or faster. In a normal application you'll never ever notice the difference. You should use the way which you think is most readable.
The first one is used to compare types not values.
If you want to compare strings with a non-sensitive case you can use:
string toto = "toto";
string tata = "tata";
bool isEqual = string.Compare(toto, tata, StringComparison.InvariantCultureIgnoreCase) == 0;
Console.WriteLine(isEqual);
How about you tell me? :)
Take the code from this Coding Horror post, and insert your code to test in place of his algorithm.
Comparing strings with a "==" operator compares the contents of the string vs. the string object reference. Comparing objects will call the "Equals" method of the object to determine whether they are equal or not. The default implementation of Equals is to do a reference comparison, returning True if both object references are the same physical object. This will likely be faster than the string comparison, but is dependent on the type of object being compared.
I'd assume that comparing the objects in your first example is going to be about as fast as it gets since its simply checking if both objects point to the same address in memory.
As it has been mentioned several times already, it is possible to compare addresses on strings as well, but this won't necessarily work if the two strings are allocated from different sources.
Lastly, its usually good form to try and compare objects based on type whenever possible. Its typically the most concrete method of identification. If your objects need to be represented by something other than their address in memory, its possible to use other attributes as identifiers.
If I understand the question and you really want to compare reference equality with the plain old "compare the contents": Build a testcase and call object.ReferenceEquals compared against a == b.
Note: You have to understand what the difference is and that you probably cannot use a reference comparison in most scenarios. If you are sure that this is what you want it might be a tiny bit faster. You have to try it yourself and evaluate if this is worth the trouble at all..
I don't feel like any of these answers address the actual question. Let's say the string in this example is the type's name and we're trying to see if it's faster to compare a type name or the type to determine what it is.
I put this together and to my surprise, it's about 10% faster to check the type name string than the type in every test I ran. I intentionally put the simplest strings and classes into play to see if it was possible to be faster, and turns out it is possible. Not sure about more complicated strings and type comparisons from heavily inherited classes. This is of course a micro-op and may possibly change at some point in the evolution of the language I suppose.
In my case, I was considering a value converter that switches based on this name, but it could also switch over the type since each type specifies a unique type name. The value converter would figure out the font awesome icon to show based on the type of item presented.
using System;
using System.Diagnostics;
using System.Linq;
namespace ConsoleApp1
{
public sealed class A
{
public const string TypeName = "A";
}
public sealed class B
{
public const string TypeName = "B";
}
public sealed class C
{
public const string TypeName = "C";
}
class Program
{
static void Main(string[] args)
{
var testlist = Enumerable.Repeat(0, 100).SelectMany(x => new object[] { new A(), new B(), new C() }).ToList();
int count = 0;
void checkTypeName()
{
foreach (var item in testlist)
{
switch (item)
{
case A.TypeName:
count++;
break;
case B.TypeName:
count++;
break;
case C.TypeName:
count++;
break;
default:
break;
}
}
}
void checkType()
{
foreach (var item in testlist)
{
switch (item)
{
case A _:
count++;
break;
case B _:
count++;
break;
case C _:
count++;
break;
default:
break;
}
}
}
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 100000; i++)
{
checkTypeName();
}
sw.Stop();
Console.WriteLine(sw.Elapsed);
sw.Restart();
for (int i = 0; i < 100000; i++)
{
checkType();
}
sw.Stop();
Console.WriteLine(sw.Elapsed);
}
}
}

Categories