I am still learning C# but I've been on annual leave and I have come back to work and seen this piece of code my senior has left me before he went on his annual leave:
public string GetBasketTotalPrice(string basketLocation)
{
var basketTotalPrice = _driver.FindElements(CommonPageElements.BasketTotalPrice);
if (basketLocation.ToLower() == "top")
return basketTotalPrice[0].Text.Replace("£", "");
else
return basketTotalPrice[1].Text.Replace("£", "");
}
private int GetElementIndexForBasketLocation(string basketLocation)
{
return basketLocation == "top" ? 0 : 1;
}
I am assuming instead of using the if else statement, he wants me to use his new method of GetElementIndexForBasketLocation.
My question is simply how to implement this change?
Thanks
It's not totally clear what you're looking for, but you can rework the code something like this:
public string GetBasketTotalPrice(string basketLocation)
{
var basketTotalPrice = _driver.FindElements(CommonPageElements.BasketTotalPrice);
int index = GetElementIndexForBasketLocation(basketLocation);
return basketTotalPrice[index].Text.Replace("£", "");
}
private int GetElementIndexForBasketLocation(string basketLocation)
{
return basketLocation.ToLower() == "top" ? 0 : 1;
}
It looks like the method he provided you isn't calling ToLower(), leaving it to the client to do that work, which could lead to mistakes down the road.
Also, you might consider using string.Equals with StringComparison.OrdinalIgnoreCase instead of ToLower.
And it might be a good idea to add a null check before trying to call a method on the string, so you can throw an ArgumentNullException instead of the NullReferenceException that the current code will throw.
public string GetBasketTotalPrice(string basketLocation)
{
var basketTotalPrice = _driver.FindElements(CommonPageElements.BasketTotalPrice);
return basketTotalPrice[GetElementIndexForBasketLocation(basketLocation)]
.Text.Replace("£", "");
}
private int GetElementIndexForBasketLocation(string basketLocation)
{
if (basketLocation == null) throw new ArgumentNullException(nameof(basketLocation));
return basketLocation.Equals("top", StringComparison.OrdinalIgnoreCase) ? 0 : 1;
}
Related
I'm new to C# but not to programming in general.
I am trying to set add some error checking to my program. There are 3 textboxes and I am trying to make it so that if the text box is left blank, it assumes a value of 0. Here is my code so far:
private void btnCalculate_Click(object sender, EventArgs e)
{
if (String.IsNullOrEmpty(txtNumberOfClassATix.Text)) // Assumes 0 if no number entered for txtNumberOfClassATix.Text.
{
txtNumberOfClassATix.Text = "0";
}
if (String.IsNullOrEmpty(txtNumberOfClassBTix.Text)) // Assumes 0 if no number entered for txtNumberOfClassBTix.Text.
{
txtNumberOfClassBTix.Text = "0";
}
if (String.IsNullOrEmpty(txtNumberOfClassCTix.Text)) // Assumes 0 if no number entered for txtNumberOfClassCTix.Text.
{
txtNumberOfClassCTix.Text = "0";
}
int classANum = int.Parse(txtNumberOfClassATix.Text);
int classBNum = int.Parse(txtNumberOfClassBTix.Text);
int classCNum = int.Parse(txtNumberOfClassCTix.Text);
double classATotal = classANum * classAPrice;
double classBTotal = classBNum * classBPrice;
double classCTotal = classCNum * classCPrice;
lblCalculatedClassARevenue.Text = $"{classATotal:c}";
lblCalculatedClassBRevenue.Text = $"{classBTotal:c}";
lblCalculatedClassCRevenue.Text = $"{classCTotal:c}";
lblCalculatedTotalRevenue.Text = $"{(classATotal + classBTotal) + classCTotal:c}";
}
This code works but I'm sure I could replace those if statements with something simpler. I've seen how to set a variable to null if another is null using the null-conditional operator but I don't really grasp it enough to adapt it to my scenario.
So far maccettura's answer is the best, but can we do better? Sure we can. Let's make a general-purpose extension method:
internal static class Extensions
{
public static int? AsInt(this string s)
{
int result;
if (s == null)
return null;
else if (int.TryParse(s, out result))
return result;
else
return null;
}
}
And now:
int classANum = txtNumberOfClassATix.Text.AsInt() ?? 0;
If it's an int, you get the int. If it's not, you get zero. Easy peasy.
Or, you might want this extension method:
internal static class Extensions
{
public static int AsInt(this string s, int default = 0)
{
int result;
if (s == null)
return default;
else if (int.TryParse(s, out result))
return result;
else
return default;
}
}
And now you can say what you want the default to be without using ??.
This style of programming is called "fluent programming"; it can make code that is very easy to read and understand.
Notice that this solution does not update the UI with zeros; if you wanted to do that then I would recommend splitting that into two steps: one which causes the mutation, and then a separate step which computes the value. Operations which are useful for both their effects and their values can be confusing.
This is a perfect time to use a method so you arent repeating yourself:
private static int GetInputAsInt(TextBox textbox)
{
int outputValue = 0;
if(textbox?.Text != null && int.TryParse(textbox.Text, out outputValue))
{
return outputValue;
}
return 0;
}
Now you are checking if the textbox itself is not null, and that the value contained therein is a int, if anything fails it returns a 0;
Call it in your other method like this:
int classANum = GetInputAsInt(txtNumberOfClassATix);
Which means your button click event would be a bit simpler:
private void btnCalculate_Click(object sender, EventArgs e)
{
int classANum = GetInputAsInt(txtNumberOfClassATix);
int classBNum = GetInputAsInt(txtNumberOfClassBTix);
int classCNum = GetInputAsInt(txtNumberOfClassCTix);
double classATotal = classANum * classAPrice;
double classBTotal = classBNum * classBPrice;
double classCTotal = classCNum * classCPrice;
lblCalculatedClassARevenue.Text = $"{classATotal:c}";
lblCalculatedClassBRevenue.Text = $"{classBTotal:c}";
lblCalculatedClassCRevenue.Text = $"{classCTotal:c}";
lblCalculatedTotalRevenue.Text = $"{(classATotal + classBTotal) + classCTotal:c}";
}
To keep it simple, a good approach is to use the conditional operator. The full example is below (broken across two lines for readability):
txtNumberOfClassATix.Text =
String.IsNullOrEmpty(txtNumberOfClassATix.Text) ? "0" : txtNumberOfClassATix.Text;
This is a nice, readable, assignment for the first part:
myString = ...
The conditional operator breaks down by providing a boolean expression (true/ false) on the left side of the ?. So, for example:
myString = anotherString == "" ? ... // checking if another string is empty
The final part is the :. To the left is the assignment if the expression is true, and to the right goes the assignment if the expression is false. To finish the example:
myString = anotherString == "" ? "anotherString is empty" : "anotherString is not empty";
The above example can be written out in full to clear up any misunderstanding as:
if (anotherString == "")
{
myString = "anotherString is empty";
}
else
{
myString = "anotherString is not empty";
}
This can apply to all the statements. The documentation is found here.
The best way to reduce the line of code is use the function for your common operation(s). In your case, you can create function which checks whether or not the object is NULL or empty. Based on the return value of that function you can proceed ahead. On the other hand, you can handle it on front-end by using different validators such as RequiredFieldValidator, CustomValidator, etc.
When using Code Contracts I get the warning:
Detected call to method
'System.Int32.TryParse(System.String,System.Int32#)' without [Pure] in
contracts of method
Having a class with interface and code contracts defined on the inteface like the code below. The question is how to check that the string orgNumberWithoutControlDigit can be converted to an valid integer, as it's a prereq for the modulus to work?
public string getControlDigit(string orgNumberWithoutControlDigit)
{
List<int> orgNumberNumbers = this.getNumberList(orgNumberWithoutControlDigit);
List<int> productList = orgNumberNumbers.Zip(this.weightNumberList, (first, second) => first * second).ToList();
int modular = productList.Sum() % 11;
string controlDigit = getControlDigit(modular);
return controlDigit;
}
private static string getControlDigit(int modular)
{
string controlDigit;
if (modular == 0)
{
controlDigit = "0";
}
else if (modular == 1)
{
controlDigit = "-";
}
else
{
int result = 11 - modular;
controlDigit = result.ToString();
}
return controlDigit;
}
[ContractClass(typeof(CalculateOrgNumberControlDigitBusinessContract))]
public interface ICalculateOrgNumberControlDigitBusiness
{
string getControlDigit(string orgNumberWithoutControlDigit);
}
[ContractClassFor(typeof(ICalculateOrgNumberControlDigitBusiness))]
public abstract class CalculateOrgNumberControlDigitBusinessContract:ICalculateOrgNumberControlDigitBusiness
{
public string getControlDigit(string orgNumberWithoutControlDigit)
{
Contract.Requires(orgNumberWithoutControlDigit.Length == 8);
int parseResult;
Contract.Requires(int.TryParse(orgNumberWithoutControlDigit, out parseResult));
Contract.Ensures(parseResult >= 0);
var result = Contract.Result<string>();
Contract.Ensures(result != null && result.Length == 1);
return default(string);
}
}
I understand what you want to achieve, but I would say that passing orgNumberWithoutControlDigit as a string to getControlDigit [sic] is the real culprit here.
Even if you could make your contract to work - the caller must also convert the string to int in order to satisfy your contract. Now if the caller already have made that conversion to an int, why not let it pass that int instead?
I am a huge fan of Code Contracts and use it in most of my projects, and I have learned that it is not a silver bullet. So if you have to have a string parameter, remove the contract altogether and simply make sure your string is in a valid format before use.
Maybe an OrgNumberValidator helper would be a better choice than relying on contracts for this?
EDIT: Actually, I would recommend creating an OrgNumber class for handling them.
You can create a pure helper method instead of calling int.TryParse directly:
[Pure]
private static bool IsInt(string s)
{
int n;
return int.TryParse(s, out n);
}
You could go further and wrap the TryParse in a try block, returning false if any type of exception is thrown (just to be on the safe side).
However, I tend to share Michael's opinion that you would do better by avoiding passing strings about to represent integers if you can.
I'm having a little difficulty with the array.sort. I have a class and this class has two fields, one is a random string the other one is a random number. If i want to sort it with one parameter it just works fine. But i would like to sort it with two parameters. The first one is the SUM of the numbers(from low to high), and THEN if these numbers are equal by the random string that is give to them(from low to high).
Can you give some hint and tips how may i can "merge" these two kinds of sort?
Array.Sort(Phonebook, delegate(PBook user1, PBook user2)
{ return user1.Sum().CompareTo(user2.Sum()); });
Console.WriteLine("ORDER");
foreach (PBook user in Phonebook)
{
Console.WriteLine(user.name);
}
That's how i order it with one parameter.
i think this is what you are after:
sourcearray.OrderBy(a=> a.sum).ThenBy(a => a.random)
Here is the general algorithm that you'll use for comparing multiple fields in a CompareTo method:
public int compare(MyClass first, MyClass second)
{
int firstComparison = first.FirstValue.CompareTo(second.SecondValue);
if (firstComparison != 0)
{
return firstComparison;
}
else
{
return first.SecondValue.CompareTo(second.SecondValue);
}
}
However, LINQ does make the syntax for doing this much easier, allowing you to only write:
Phonebook = Phonebook.OrderBy(book=> book.Sum())
.ThenBy(book => book.OtherProperty)
.ToArray();
You can do this in-place by using a custom IComparer<PBook>. The following should order your array as per your original code, but if two sums are equal it should fall back on the random string (which I've called RandomString):
public class PBookComparer : IComparer<PBook>
{
public int Compare(PBook x, PBook y)
{
// Sort null items to the top; you can drop this
// if you don't care about null items.
if (x == null)
return y == null ? 0 : -1;
else if (y == null)
return 1;
// Comparison of sums.
var sumCompare = x.Sum().CompareTo(y.Sum());
if (sumCompare != 0)
return sumCompare;
// Sums are the same; return comparison of strings
return String.Compare(x.RandomString, y.RandomString);
}
}
You call this as
Array.Sort(Phonebook, new PBookComparer());
You could just do this inline but it gets a bit hard to follow:
Array.Sort(Phonebook, (x, y) => {
int sc = x.Sum().CompareTo(y.Sum());
return sc != 0 ? sc : string.Compare(x.RandomString, y.RandomString); });
... Actually, that isn't too bad, although I have dropped the null checks.
I have been up half the night and still trying to get this null exception figured out. I have read a few of texts about this issue but none has helped me in any way, to me what the problem is as it should work :/ It just crashes at this piece of code:
Private void UpdateGUI()
{
string selectedItem = cmbDisplayOptions.Items[cmbDisplayOptions.SelectedIndex].ToString();
rdbtReserv.Checked = true;
lstReservations.Items.Clear();
lstReservations.Items.AddRange(m_seatMngr.GetSeatInfoStrings(selectedItem));
}
lstReservations.Items.AddRange(m_seatMngr.GetSeatInfoStrings(selectedItem)); Gives me the ArgumentNullExeption, but to me it should not do that.
the addrange sends string selectedItem to another class:
public string[] GetSeatInfoStrings(string selectedItem)
{
int count = GetNumOfSeats(selectedItem);
if (count <= 0)
{
return null;
}
string[] strSeatInfoStrings = new string[count];
for (int index = 0; index <= m_totNumOfSeats - 1; index++)
{
strSeatInfoStrings[index] = GetSeatInfoAt(index);
}
return strSeatInfoStrings;
}
This int count = GetNumOfSeats(selectedItem); goes to here and returns with an int:
private int GetNumOfSeats(string selectedItem)
{
if (selectedItem == "ReservedSeats")
{
return GetNumReserved();
}
if (selectedItem == "VacantSeats")
{
return GetNumVacant();
}
else
{
return m_totNumOfSeats;
}
}
I have checked the arrayed have the correct number of spaces(60) and that selectedItem has a string(Allseats to start with so it should return m_totnumOfSeats which is an int of 60) But then in the private int GetNumOfSeats something goes wrong and it returns null and...well why?
I can't see the problem.. maybe gone blind by trying to find the issue. Always got outstanding help here and I have learned tons!! So maybe someone can point out all the issues there is in my code.
Thanks a million in advance for any and all advice!
//Regards
Check if your variables are actually initialized and returns correct values.
There are logical errors in the GetSeatInfoStrings methods and the GetNumofSeats method.
Lucky for you the GetNumOfSeats method will always return 60 for you because of the wrong way you compare strings. It's not the right way, so use the Equals method for comparison like
if (selectedItem.Equals("ReservedSeats"))
With that you will get a proper output form GetNumOfSeats(string) method.
The next thing is to fix your looping in the GetSeatInfoStrings method so as to not get an array index out of bounds exception like this.
string[] strSeatInfoStrings = new string[count];
for (int index = 0; index <= count; index++)
{
strSeatInfoStrings[index] = GetSeatInfoAt(index);
}
return strSeatInfoStrings;
Also fix the part where your logic returns a null in the GetSeatInfoStrings method. it should return an empty string array according to your logic as
return new string[0];
That should probably get your methods working. You need to be very careful of what you code before you debug it :-)
Looking at the source code of ObjectCollection, when you call AddRange and pass a null value, you get back the ArgumentNullException.
You could prevent this changing this code
if (count <= 0)
{
return new string[0];
}
I came across the following expression in someone else's code. I think it's terrible code for a number of reasons (not least because it fails to take into account bool.TrueString and bool.FalseString), but am curious as to how the compiler will evaluate it.
private bool GetBoolValue(string value)
{
return value != null ? value.ToUpper() == "ON" ? true : false : false;
}
Edit
Incidentally, aren't the expressions evaluated from the inside-outwards? In this case, what's the point of checking for value != null after the call to value.ToUpper() which will throw a null reference exception?
I think the following is a correct (deliberately) verbose version (I'd never leave it like this :D ):
if (value != null)
{
if (value.ToUpper() == "ON")
{
return true;
}
else // this else is actually pointless
{
return false;
}
}
else
{
return false;
}
Which can be shortened to:
return value != null && value.ToUpper == "ON";
Is this a correct re-writing of the expression?
It looks like the method is indended to handle a value that comes from a checkbox HTML element. If no value is specified for the checkbox, it uses the value "on" by default. If the checkbox is not checked there is no value at all from it in the form data, so reading the key from Request.Form gives a null reference.
In this context the method is correct, althought it's quite horrible due to the use of the if-condition-then-true-else-false anti-pattern. Also it should have been given a name that is more fitting for it's specific use, like GetCheckboxValue.
Your rewrite of the method is correct and sound. As the value is not culture dependant, converting the value to uppercase should not use the current culture. So a rewrite that is even slightly better than the one that you proposed would be:
return value != null && value.ToUpperInvariant == "ON";
(The culture independent methods are also a bit faster than the ones using a specific culture, so there is no reason not to use them.)
Incidentally, aren't the expressions
evaluated from the inside-outwards?
If it was method calls so that all expressions were actually evaluated, they would, as the inner call has to be made to evaluate the parameters for the outer call.
However, the second and third operands of the conditional expression is only evaluated if they are used, so the expressions are evaluated from the outside and inwards. The outermost condition is evaluated first to decide which of the operands it will evaluate.
You are correct, both in your rewriting and in your assertion that this attempt at conciseness is bad because it leads to confusion.
well the first one is a double-nested ternary operator
return (value != null) ? [[[value.ToUpper() == "ON" ? true : false]]] : false;
The bit in [[[ ]]] is the first result of the ternary expression which gets evaluated
when the first condition is true so you're reading/assertion of it is correct
but its ugly as hell and very unreadable/unmaintainable in its current state.
I'd definitely change it to your last suggestion
SideNote:
People who do
if(X == true)
return true;
else
return false;
instead of
return X;
should be taken out and shot ;-)
Are you looking for speed or readability and organization? Speed of execution, your shortened example is probably the best way to go.
For a few extra milliseconds, you could re-write this utility method as an extension method like so:
public static bool ToBoolean(this string value)
{
// Exit now if no value is set
if (string.IsNullOrEmpty(value)) return false;
switch (value.ToUpperInvariant())
{
case "ON":
case "TRUE":
return true;
}
return false;
}
... and then you would use it as follows:
public static void TestMethod()
{
bool s = "Test".ToBoolean();
}
EDIT:
Actually, I'm wrong... a quick performance test shows that the extension method is FASTER than the inline method. The source of my test is below, as well as the output on my PC.
[Test]
public void Perf()
{
var testValues = new string[] {"true", "On", "test", "FaLsE", "Bogus", ""};
var rdm = new Random();
int RunCount = 100000;
bool b;
string s;
Stopwatch sw = Stopwatch.StartNew();
for (var i=0; i<RunCount; i++)
{
s = testValues[rdm.Next(0, testValues.Length - 1)];
b = s.ToBoolean();
}
Console.Out.WriteLine("Method 1: {0}ms", sw.ElapsedMilliseconds);
sw = Stopwatch.StartNew();
for (var i = 0; i < RunCount; i++)
{
s = testValues[rdm.Next(0, testValues.Length - 1)];
b = s != null ? s.ToUpperInvariant() == "ON" ? true : s.ToUpperInvariant() == "TRUE" ? true : false : false;
}
Console.Out.WriteLine("Method 2: {0}ms", sw.ElapsedMilliseconds);
}
Output:
Method 1: 21ms
Method 2: 30ms
I read the original expression the same way you do. So I think your shortened expression is correct. If value is null it will never get to the second conditional, so it looks safe to me.
I also hate the constructs like:
if (value.ToUpper() == "ON")
{
return true;
}
else // this else is actually pointless
{
return false;
}
as you noticed it is a long and convoluted (not to say stupid) way of writing:
return value.ToUpper() == "ON";
Your proposition is nice, short and correct.
Another alternative:
return string.Equals( value, "ON", StringComparison.OrdinalIgnoreCase );