I have an XML file that contains a "script" of items to check and validate. What it does is reads in a value to check, and if that check is true, it does something. I originally wrote this to work with just integers, but I realize I need to make it work with more data types.
A sample check is like this...It sees if SomeValue is greater than 20.
<If field="SomeValue" test="#gt" value="20" />
*The field is just some string value. So for a double, the field would be something like 55.7.
All I do is do a int.TryParse on the value to see if I can cast the string (SomeValue) to an integer. If I can, I check to see if it is greater than 20. If not, I just assume false on the check.
Does anyone have any suggestions on how I could this with any data type? (i.e. string, double, DateTime)
Would Generics work? I have never used them so I dont know if they would be the best solution. Thanks.
The tricky bit is a parse when you don't know the types, but this can be done with TypeDescriptor.GetConverter:
object knownVal = 21; //perhaps obtained from reflection
Type type = typeof(int);
string text = "20";
object val = TypeDescriptor.GetConverter(type)
.ConvertFromInvariantString(text);
int rel = Comparer.Default.Compare(knownVal, val);
Generics is an option (especially with Comparer<T>.Default.Compare), but generics doesn't mix well with Type values only known at runtime. It can be done (MakeGenericType/MakeGenericMethod), but it is ugly and a bit slow.
To be honest, though: if it was me I would assume there is a small number of types that need handling here, and special-case them.
Related
I'm reviewing code review suggestions written from/to various developers and came across an interesting one.
Someone originally wrote a basic comparison in LINQ (EF to be specific):
myTable.Where(i => i.MyValue == 1);
Where 1 is an unchanging TypeId stored in the database.
The suggestion was to remove the hard coded value of 1 in favor of a const. So for example it would be rewritten as:
const int valueId = 1;
myTable.Where(i => i.MyValue == valueId);
From the suggestion point of view I get where they were coming from with the const since the code only ever needs a single copy of this value. But perhaps the compiler is smart enough to recognize that this is an unchanging value and treats it similarly.
So the question remains, does this kind of code refactor actually hold any weight other than eliminating "magic numbers"?
At this level, it is highly unlikely it matters what is produced at the compiler. It is likely the same anyway. The point is, what is safer for usage and easier to understand? Does '1' represent anything in particular except the literal value '1'? Based on the code snippet, I would guess that it does, and that is very good grounds for introducing a constant field because you now know exactly what is being checked against here.
Is this literal value '1' used in any other places that would need to be changed if the value change, for example, to '2'? If so, that also is very good grounds for introducing a constant field because now you only have to change the value in a single place, rather than search your entire code base, and most likely missing at least one instance.
Also, credit to Ixrec from Programmers, valueId is a terrible name for a constant as it does not say what the value is. A better name would be answersId, if, for example, the '1' represented answers while '0' represented questions and '2' represented comments.
First of all, both versions of the code will compile to exactly the same IL.
A constant is not a variable. Any usage of a constant is replaced at compile-time by it's value.
There are 2 advantages of using a const instead of a literal
The constant can be defined once and used in many places. So if you ever need to change the value of the constant, you only need to change it in one place*
You can give a meaningful name to the constant.
(*) Never change value of a public const field - all other assemblies using this constant will have to be recompiled to use the updated value.
I have Sharepoint list values that are Currency, such as:
list.Fields.Add("Airfare", SPFieldType.Currency, false);
But how do I save these values into the list as the Currency type?
I tried this:
spli["Airfare"] = (SPFieldType.Currency) boxAirfare.Text;
..and this:
spli["Airfare"] = boxAirfare.Text as SPFieldType.Currency;
...but they won't compile, telling me, "'Microsoft.SharePoint.SPFieldType.Currency' is a 'field' but is used like a 'type'"
I then tried to Convert the val, but found, much to my disjune (it's too late in the year for dismay), that there is no Currency type there. And even what seems to be the next best (or even better, if not for this) choice, namely Decimal, hints at a dismal destiny for such an attempt:
Is this true -- that converting to Decimal always fails? Note: "Convert.ToDouble" exudes the same dire warning.
Am I doomed to store the vals as String, and do the hocus pocus as necessary to treat them as money vals?
Note: A related question about which data type to use in Sharepoint for money is here
Convert.ToDouble() should work fine (or double.Parse()).
The warning you see is related to trying to convert a DateTime to a decimal/double. If you look at the overloaded version that converts a string, I suspect you'll see no such warning.
Try:
//assumes input has been validated
spli["Airfare"] = double.Parse(boxAirfare.Text);
I've run into this pattern repeatedly in a project I'm working with:
int myIntValue = int.Parse(myNumericUpDown.Value.ToString());
This seems a little bit bananas to me, get a string from a decimal and then parse the string to get an int, and I wonder if there is something I'm missing that necessitates it. Seems like it has to be deliberate. Is there a reason that should be used rather than the obvious approach:
int myIntValue = (int)myNumericUpDown.Value;
Or:
int myIntValue = Convert.ToInt32(myNumericUpDown.Value);
Of course we cannot look into the mind of the programmer who wrote that code initially, but doing numeric conversions through strings is quite common with newer programmers. Presumably they first wrote
int myIntValue = myNumericUpDown.Value
got a compiler error, found Int32.Parse and put the pieces together.
There is really no reason at all to convert it to a string first, and then parse an integer from that - if anything it is inefficient and awkward to read.
In fact note that if for whatever reason the ToString gives an actual decimal number (like 3.0 instead of 3) the code will throw a System.FormatException which is not caught. From the assumption that you are not seeing that exception when running, I deduce that the control is set such that the Value property is always an integer, never something like 1.932, hence I would argue that the fastest way to get it as an integer is a hard cast
int myIntValue = (int)myNumericUpDown.Value;
(and add a try/catch for the inevitable case that your form designer messes up and sets the initial value of the control to 0.5).
I realize that I can select an element from an array from an enum by casting it as an int, but since I need to do that numerous times in my code, I was wondering if there is a way to set up a property or something like that to reduce code duplication. How would I be able to do something like this?
Casting an enum to an int extremely efficient. In fact, in most cases, the compiler does most of the work for you already. For example, using a constant like this arr[(int)MyEnum.Value] will be automatically compiled into an int literal like arr[7].
So there's really no need to try to speed this up.
However, if your enum values do not strictly range from 0 — n, (where n < array.length), then suggest using a Dictionary instead.
I just need to know if the value is numeric. I don't need to do anything with the value. Is this the best way? Feel dirty creating a variable that I won't ever use beyond this:
int val;
if(int.TryParse(txtFoo.Text, out val))
{
....
}
Yes, using the relevant TryParse method and ignoring the out parameter is the best way of doing this.
You may want to wrap this up into your own set of helper methods (which could specify the appropriate culture etc, if the default isn't right for you) and just return a bool without the out parameter to make them easier to call.
Of course, you need to work out what kind of parsing is most appropriate - even for integers, you need to consider whether the range of Int32 is enough for your use case. In my experience, most numeric input has its own "natural" range of valid values, which is unlikely to be exactly the range of any predefined type. You may therefore want to expand your helper methods to include the range of valid values to accept.
"is numeric" is an ambiguous term.
Culture-aware?
Allow thousands and/or decimal separators?
Allow scientific notation?
Allow a sign (before? after?...)
What range of values do you allow? Signed 32-bit integer (Int32.TryParse), Unsigned 32-bit integer (UInt32.TryParse), decimal, double, ...
Hence there is no "best" way, and the Framework provides a multitude of different ways to parse numbers.
You can use Regular expressions
Regex _isNumber = new Regex(#"^\d+$");
_isNumber.IsMatch(txtFoo.Text);
This will only match Ints, but you can write one that also matches decimals.
It's not as flexible as int.TryParse, but you could check to see if each character is a number:
bool isInt = txtFoo.Text.All(c => char.IsNumber(c));
In general, though, I would recommend sticking with int.TryParse. You can even call the unused parameter "ignored" to be explicit about your intent, e.g.:
int ignored;
bool isInt = int.TryParse(txtFoo.Text, out ignored);
That is the recommended way of doing it in C#. However, you could also add Microsoft.VisualBasic.dll as a reference to your project and then use Microsoft.VisualBasic.Information.IsNumeric()
You can try using Regex parsing to determine that there are no non-numeric characters in a string, or you can use Int.TryParse(), Double.TryParse(), Float.TryParse() depending on the input.
bool test (string teststring)
{ for (i=0;i==teststring.length;i++){
if instr("0123456789.,-+Ee",teststring.substring(i,1) <0){return false;}
// some additional tests below here if you like
return true;
}
however E1001E12e.12e would be noted as a number a little bit more magic is needed to do a clean check, but then you might be able to determine if its a int or a float too..
That's the best way of doing it in my knowledge - that's what our company standards adhere to anyway due to the error handling being done within the parsing.
This details the advantages: https://web.archive.org/web/20150510214425/http://www.dotnetperls.com:80/int-tryparse