How to refactor the code which uses nested if-elses? - c#

I have some code written in C# for which I need to add some conditions. From one of my methods, I'm calling another method "UpdateData()", which updates some data into the database. Now for this, I need to add some conditions.
There will be appsetting in the config file, which is a boolean value. If the value is true, then the conditions in point2 have to be checked, if the value is false, it has to directly call UpdateData() method.
There are few types and statuses which needs be checked if the point1 is true.
Condition 1: If type is 'A' and status is "Complete" - then call "UpdateData()".
Condition 2 : If type is 'B' and status is "Complete" or "Partial" or "some xyz" - then call "UpdateData()".
So for this I have written the below code :
bool checkStatus = Convert.ToBoolean(ConfigurationManager.AppSettings["CheckStatus"]);
if (checkStatus)
{
if (type == "A" && status == "Complete")
{
UpdateData();
}
else if (type == "B" && (status == "Complete" || status == "Other status" || status == "someother status"))
{
UpdateData(); // for type B , data should not be updated if status is anything other than those in condition.
}
}
else
{
UpdateData();
}
This is working as expected. But I do not want to write nested if-else. If tomorrow there is some other status and type I need to include, I need to add another if condition here.
Is there any better or optimized way of writing it?

I personally think what you have is fine. However, if you really don't like it, you could fiddle with local methods i guess
bool CheckA() => type == "A" && status == "Complete";
bool CheckB() => type == "B" && (status == "Complete" || status == "Other status" || status == "someother status")
if (!checkStatus || CheckA() || CheckB())
UpdateData();
or
bool Check(string val, params string[] list) => type == val && list.Contains(status);
// uglay, yet approachable
if (!checkStatus ||
Check("A", "Complete") ||
Check("B", "Complete", "Other status", "someother status"))
{
UpdateData();
}
Note : I personally wouldn't do these, though it might be your cup of tea

If it is to optimise the ability to add a new one quickly, I would've put it in an array.
bool[] checks = new bool[]{
(type == "A" && status == "Complete"),
(type == "B" && (status == "Complete" || status == "Other status" || status == "someother status"))
}
if (!checkStatus || checks.Contains(false)) //'Contains' requires Linq, it also works with 'checks.Any(c => !c)'
{
UpdateData();
}
This way you only need to add a new condition in the bool array and you don't have to update the if-statement for it. And you can comment out one quickly if you don't need it.
Though, it's usefulness depends on how many conditions you may expect to add.

I guess the main point of this code is to ensure the update runs when the conditions are met, which is done via unit testing.
So, I would look at how I could write this code in a testable manner so I can be sure that it does what it says on the tin.
One way to do it could be something like this:
public bool AppSettingsRunStatusMet(string appSettingsCheckStatus)
{
return Convert.ToBoolean(appSettingsCheckStatus);
}
public bool TypeARunStatusMet(string type, string status)
{
return (type.Equals("A") && status.Equals("Complete"))
}
public bool TypeBRunStatusMet(string type, string status)
{
return (
type.Equals("B") &&
(
status.Equals("Complete") ||
status.Equals("Other status") ||
status.Equals("someother status")
)
}
This is starting to shape up as a rules engine and you can have as many rules as you need
each if statement can now call one method and each rule is separated in its own method which can now be easily tested properly.
You could put all rules in their own class(es), have the engine return a status and if that status is true then run the update. The main idea is to separate config code, from the status analysis and from the code which runs the update and does whatever it is it needs to do.

I'd prefer doing something like this, so that when new cases shows up or validation criteria changes we can just whitelist new case. This is simple/maintainable, works well for growing set of valid_cases with little changes.
using System;
using System.Collections.Generic;
public class Program
{
class Case{
string type;
string status;
public Case(string type_val, string status_val ){
status = status_val;
type = type_val;
}
public override int GetHashCode()
{
return type.GetHashCode()+status.GetHashCode();
}
public override bool Equals(object obj)
{
Case test = obj as Case;
if (test== null)
return false;
return type == test.type && status == test.status ;
}
}
public static void Main()
{
bool checkStatus = true;
HashSet<Case> vaild_cases = new HashSet<Case>();
vaild_cases.Add(new Case("A","Complete"));
vaild_cases.Add(new Case("B","Complete"));
vaild_cases.Add(new Case("B","Other status"));
vaild_cases.Add(new Case("B","someother status"));
Case current_case = new Case("A","Complete");
if (!checkStatus || vaild_cases.Contains(current_case))
UpdateData();
}
static void UpdateData()
{
Console.WriteLine("Hello, World!");
return;
}
}
Cheers !!

Another option is this:
if((new string[] {"Complete", "Other status", "someother status"}.Contains(status) && type == "B") ||
(status == "Complete" && type == "A") ||
!checkStatus)
{
UpdateData();
}

Related

Replacing conditional with something?

I have the following nested if clause, I was wondering if I, by applying some pattern, can simplify it?
The code checks to see if it needs an authorizationStartDate, and if it does, but does not have it, returns true.
I've considered the Strategy Pattern, the "Replace conditional with polymorphism" method, the Specification Pattern and various others, but I haven't found anything which I liked.
private bool IsMissingAuthorizationStartDate(ApplicationStatusData data)
{
if (data.ApplicationStatus == ApplicationStatusType.ApplicationApproved)
{
if (data.ApplicationPurpose == ApplicationPurpose.New)
{
if (data.ProductStatus?.ProductStatusType == ProductStatusType.ApplicationForNewProductReceived)
{
if (data.ApplicationTypePesticide == ApplicationTypePesticide.Authorisation ||
data.ApplicationTypePesticide == ApplicationTypePesticide.ProvisionalAuthorisation ||
data.ApplicationTypePesticide == ApplicationTypePesticide.MutualRecognition ||
data.ApplicationTypePesticide == ApplicationTypePesticide.Derogation ||
data.ApplicationTypePesticide == ApplicationTypePesticide.DispensationPreviousAssessment ||
data.ApplicationTypePesticide == ApplicationTypePesticide.ResearchAndDevelopmentPurposesExperimentOrTestProgram ||
data.ApplicationTypePesticide == ApplicationTypePesticide.ResearchAndDevelopmentPurposesExperimentOrTestProgramKnownProduct ||
data.ApplicationTypePesticide == ApplicationTypePesticide.ParallelTradePermit ||
data.ApplicationTypePesticide == ApplicationTypePesticide.Copy
)
{
if (!data.AuthorizationStartDate.HasValue)
{
return true;
}
}
}
}
else if (data.ApplicationPurpose == ApplicationPurpose.Renewal)
{
if (data.ProductStatus.ProductStatusType == ProductStatusType.ProductAuthorised)
{
if (data.ApplicationTypePesticide == ApplicationTypePesticide.ReAuthorisation ||
data.ApplicationTypePesticide == ApplicationTypePesticide.ParallelTradePermit ||
data.ApplicationTypePesticide == ApplicationTypePesticide.Copy
)
{
if (!data.AuthorizationStartDate.HasValue)
{
return true;
}
}
}
}
}
// else
return false;
}
The pattern I would use here is just encapsulation. The nesting here is hard to follow, and worsened by the equality comparisons. If possible, instead of exposing the raw field, try encapsulating the intent.
e.g. Instead of if (data.ApplicationPurpose == ApplicationPurpose.Renewal) try extending ApplicationStatusData with a property like
bool IsRenewalApplication
{
get
{
return this.ApplicationPurpose == ApplicationPurpose.Renewal;
}
}
so your code reads cleaner, with more expression: if (data.IsRenewalApplication) { ... }
Particularly where you have that massive if this or that or that or that, put it under a well-named property like IsInterestingPesticide.
If you can't change ApplicationStatusData for some reason, you can do the same thing with member functions that return Boolean values, expressing the same intent.
HTH!
PS, You might even want to encapsulate the entirety of the nested-ifs into a single concept. Then you'd just have 2 Boolean tests before you return false.
I suspect you might want to take a look at the next level up in the code, the fact its returning a boolean indicates this is being used in a conditional by something else.
That said I do usually like the chain of responsibility pattern for this sort of thing. But I personally wouldn't have it return a boolean, I'd have the responsible object perform an action if it was determined to be responsible for that type of data (i.e. Another level up).
Just an option for you to consider, there isn't a hard and fast rule for this kind of thing.
This doesn't really answer to your question which was about design pattern, but that might still interest you.
You could rewrite your method like that.
First two arrays:
private ApplicationTypePesticide[] typePesticidesNewPurpose
= new ApplicationTypePesticide[]
{
ApplicationTypePesticide.Authorisation,
ApplicationTypePesticide.ProvisionalAuthorisation,
ApplicationTypePesticide.MutualRecognition,
ApplicationTypePesticide.Derogation,
ApplicationTypePesticide.DispensationPreviousAssessment,
ApplicationTypePesticide.ResearchAndDevelopmentPurposesExperimentOrTestProgram,
ApplicationTypePesticide.ResearchAndDevelopmentPurposesExperimentOrTestProgramKnownProduct,
ApplicationTypePesticide.ParallelTradePermit,
ApplicationTypePesticide.Copy
};
private ApplicationTypePesticide[] typePesticidesRenewalPurpose
= new ApplicationTypePesticide[]
{
ApplicationTypePesticide.ReAuthorisation,
ApplicationTypePesticide.ParallelTradePermit,
ApplicationTypePesticide.Copy
};
Then your previous method becomes:
private bool IsMissingAuthorizationStartDate(ApplicationStatusData data)
{
return data.ApplicationStatus == ApplicationStatusType.ApplicationApproved
&& (IsMissingAuthorizationStartDatePart2(data, ApplicationPurpose.New,
ProductStatusType.ApplicationForNewProductReceived, typePesticidesNewPurpose)
|| IsMissingAuthorizationStartDatePart2(data, ApplicationPurpose.Renewal,
ProductStatusType.ProductAuthorised, typePesticidesRenewalPurpose));
}
private bool IsMissingAuthorizationStartDatePart2(ApplicationStatusData data,
ApplicationPurpose purpose, ProductStatusType statusType,
params ApplicationTypePesticide[] typePesticides)
{
return (data.ApplicationPurpose == purpose
&& data.ProductStatus.ProductStatusType == statusType
&& statusType.Any(st => data.ApplicationTypePesticide == st)
&& !data.AuthorizationStartDate.HasValue);
}
Note: you can remove the params keyword if you always call the method like in this example.
You should also think about rename the part2 method.

Storing a complex boolean in a variable

I have a very lengthy boolean variable which looks something like:
c.Card != null && slot.Card.CardId == c.Card.CardId && slot.E1Number == c.E1Number && slot.Capacity == c.Capacity && slot.PacketLinkCapacity == c.PacketLinkCapacity && slot.TrafficType == c.TrafficType && slot.TxFrequency == c.RxFrequency && slot.RxFrequency == c.TxFrequency && slot.E1Number != null && slot.Capacity != null && slot.ProtectionMode == c.ProtectionMode
Since this condition needs to be checked frequently I keep writing the same thing over and over again. Is it possible to store this in a variable so I can just reuse that whenever I need?
Yes, you can. Just create a method that does this check, or if you want to pass that function around, use Func<bool>:
Func<bool> f = new Func<bool>( () => YourLengthyMethod() );
Try something like this:
private static Expression<Func<Slot, bool>> Filter(filter)
{
return cat => [...your code for filtering...];
}
I would recommend you to separate this and write it to another method that you will call it when you want to do all these checks. It's not good practice to have repeated code in your project.
For example, in your case, you can give as parameters to this method the info (c.Card, slot.Card.CardId etc) and do the right checking over there.
Smart(ish) usage of C# features:
class EvaluatedBoolean
{
private readonly Func<bool> _evaluation = () => false;
public EvaluatedBoolean(Func<bool> evaluation)
{
_evaluation = evaluation;
}
public static implicit operator bool(EvaluatedBoolean eb)
{
return eb._evaluation.Invoke();
}
}
Then:
var eval = new EvaluatedBoolean(() => /*your conditions here*/);
...
PerformSomeWorkIfCondition(eval);
...
void PerformSomeWorkIfCondition(bool condition)
{
if (condition)
{
//do something
...

How a Boolean method should not return for case sensitive c#

I have a Boolean function which checks the names from database, If any name exists in database which returns true. my function is
public bool rawMtrlExists(string strRawMtrl)
{
var rwMtrl = prodctsDC.productsnrwmtrls.Where(c => c.item_Ctgry == 'R' || c.item_Ctgry == 'B'||c.item_Ctgry=='G').Distinct().ToArray();
return rwMtrl.Count(d => d.item_Name == strRwMtrl) > 0;
}
My problem is.. If my strRawMtrl orgument having the same word but with casesensitive, the method returning false. I mean name in database field is Central Processing Unit. Iam passing as central processing unit. In this case It is returning false but I need true. Please anybody help me to return not for case sensitive words too.
You can use overloaded Equals method with StringComparison.OrdinalIgnoreCase:
d.item_Name.Equals(strRwMtrl, StringComparison.OrdinalIgnoreCase)
Another hacky way which I don't prefer since it creates two more strings in heap:
d.item_Name.ToLower() == strRwMtrl.ToLower()
I think you can use this. Using ToArray() forces next parts of query to run locally. Also Disticnt() redundant.
public bool rwMtrlExists(string strRwMtrl)
{
return prdTcnDC
.productsnrwmtrls
.Where(c => c.item_Ctgry == 'R'
|| c.item_Ctgry == 'B'
|| c.item_Ctgry == 'G')
.Any(d => d.item_Name.ToLower() == strRwMtrl.ToLower());
}

FindAll multiple conditions

So I'm trying to use FindAll to return a list of objects that match. It works great when I only use one condition, for example
patientstatus = statuslist.FindAll(delegate(StatusReader.onestatus p1)
{
return p1.WL_ID == patlist[i].wl_id;
});
But I would like to use multiple conditions, some of which contain if statements and I can't figure out how. It seems like the format needs to have a return statement, and the example from Microsoft (http://msdn.microsoft.com/en-us/library/fh1w7y8z.aspx) only uses one condition.
I could either have multiple FindAll methods for each condition, or just make a loop that scans through all the values myself, but there's got to be an obvious thing I'm missing, right?
I'm not quite sure what "if loops" are, but you can always just stitch them together:
patientstatus = statuslist.FindAll(delegate(StatusReader.onestatus p1)
{
if(p1.WL_ID != patlist[i].wl_id)
return false;
if(otherStuff != 5)
return false;
for(var x in p1.stuff)
if(x == 7)
return false;
return true;
});
Try the following:
var patientStatus = statusList.Where(p => p
{
// Put in logic here as you need
return p.WL_ID == patlist[i].wl_id || p.YourSecondProperty == WhateverYouWantToCheck;
}
You can think about something like
public abstract class Condition
{
public abstract bool Sutisfied(StatusReader.onestatus status);
}
public class Condition1 : Condition
{
public override bool Sutisfied(StatusReader.onestatus status) {
//check for something and return
}
}
public class Condition2 : Condition
{
public override bool Sutisfied(StatusReader.onestatus status) {
//check for something and return
}
}
After can have a list of conditions, like
List<Condition> conditions =
new List<Condition>{new Conditon1(), new Condition2()}
and after this list use inside
patientstatus = statuslist.FindAll(delegate(StatusReader.onestatus status)
{
return conditions.TrueForAll(c=>c.Sutisfied(status));
});
Your delegate simply needs to return true for a match to your conditions and false for a non-match to your conditions. It doesn't have to be a single line return statement. You can create boolean values, have if statements, for loops, and anything else you want in your delegate - so long as it returns true or false along all code paths.
So you can do as many if statements or loops as you want.
If you really want to maintain it as one line, you can do something like the following...
return (condition1 == condition1) || (condition2 == condition2) || (condition3 == condition3);

Using Try Catch to find Boolean value

I have several bool checks against values. I wanted to check in ANY of the values return false then do something.
I attempted the follow:
bool formIsValid = true;
try{
Utility.testStringHasValue(txEmail.Text); <--- true
Utility.testStringHasValue(txFirstName.Text); <--- true
Utility.testStringHasValue(txLastName.Text); <--- FALSE
Utility.testStringHasValue(txUserEmployer.Text); <--- true
Utility.testStringHasValue(txUserPassword.Text); <--- true
Utility.testStringHasValue(txUserPassword2.Text); <--- true
}
catch (Exception)
{
formIsValid = false
}
.. any other solutions as this one does not at all!
UPDATE
I removed the Try/Catch completely and used:
bool isFormValid = Utility.testStringHasValue(txEmail.Text)
&& Utility.testStringHasValue(txFirstName.Text)
&& Utility.testStringHasValue(txLastName.Text)
&& Utility.testStringHasValue(txUserEmployer.Text)
&& Utility.testStringHasValue(txUserPassword.Text)
&& Utility.testStringHasValue(txUserPassword2.Text)
&& (txUserPassword.Text == txUserPassword2.Text);
Dont use exceptions for that scenario, when an exception is thrown there is a performance hit.
There is also a method available already to check if a string is entered ( String.IsNullOrEmpty )
You could use the && (and) operator to validate all fields are valid.
eg:
bool isValid = !String.IsNullOrEmpty(txtEmail.Text) && !String.IsNullOrEmpty(txtFirstname.Text) && !String.IsNullOrEmpty(txtLastName.Text);
Alternatively, you could flip it the other way using the || (or) operator
bool isInvalid = String.IsNullOrEmpty(txtEmail.Text) || String.IsNullOrEmpty(txtFirstname.Text) || String.IsNullOrEmpty(txtLastName.Text);
If you wrote the Utility methods, then I would suggest making sure it won't throw an exception, especially if it returns a boolean indicating whether the string has a value.
You could just do the following, and avoid the try { } catch { } entirely.
bool formIsValid = Utility.testStringHasValue(txEmail.Text); <--- true
&& Utility.testStringHasValue(txFirstName.Text); <--- true
&& Utility.testStringHasValue(txLastName.Text); <--- FALSE
&& Utility.testStringHasValue(txUserEmployer.Text); <--- true
&& Utility.testStringHasValue(txUserPassword.Text); <--- true
&& Utility.testStringHasValue(txUserPassword2.Text); <--- true
&& will not call evaluate the right side unless the left side returns true, so Utility.testStringHasValue will only actually be called 3 times in your example.
if ( Utility.testStringHasValue(txEmail.Text) &&
Utility.testStringHasValue(txFirstName.Text) &&
Utility.testStringHasValue(txLastName.Text) &&
Utility.testStringHasValue(txUserEmployer.Text) &&
Utility.testStringHasValue(txUserPassword.Text) &&
Utility.testStringHasValue(txUserPassword2.Text)
) {
all_good = true;
} else {
no_so_good = true;
}
Exception handling should not be used as control logic. It obfuscates the purpose.
That's because returning false doesn't throw an exception. You could always do:
if (!(
Utility.testStringHasValue(txEmail.Text) &&
Utility.testStringHasValue(txFirstName.Text) &&
Utility.testStringHasValue(txLastName.Text) &&
Utility.testStringHasValue(txUserEmployer.Text) &&
Utility.testStringHasValue(txUserPassword.Text) &&
Utility.testStringHasValue(txUserPassword2.Text)))
{
throw new Exception("Something is false");
}
if ( !(condtion1 && condition2 && .... && conditionN) )
{
// first condition evaluating to false gets you here
// anything afterwards is not checked.
}
Recode your Utility.testStringHasValue() to return false, not throw an exception.
Next
formIsValid= Utility.testStringHasValue(txEmail.Text) &&
Utility.testStringHasValue(txFirstName.Text) &&
Utility.testStringHasValue(txLastName.Text) &&
Utility.testStringHasValue(txUserEmployer.Text) &&
Utility.testStringHasValue(txUserPassword.Text) &&
Utility.testStringHasValue(txUserPassword2.Text) ;
if (formIsValid) {
//whatever
} else {
//whatever
}
What does testStringHasValue return? if a boolean then just use all the combined return values instead, like this :
boolean result = true;
result = result && Utility.testStringHasValue(txEmail.Text);
result = result && Utility.testStringHasValue(txFirstName.Text);
return result;
The way you are using exceptions is not very good. They should not be used for this kind of validation and your intention to reach the catch will only be valid if you actuelly throw a exception not onyl because it returns a boolean false value.
try..catch exception handling is more expensive than a simple boolean check, resource-wise. From the msdn site re: try..catch: "Catching exceptions at runtime creates additional overhead, and is likely to be slower than pre-checking to avoid exceptions."
Use if..then as shown previously.
Have you thought about writing a reusable method to check all the values passed to it aren't empty? Something like this could be a major time saver if you are using the code in multiple places:
using System.Linq;
public static class Utility
{
public static bool EnsureValuesNotEmpty(params string[] values)
{
return values.All(value => !string.IsNullOrWhiteSpace(value));
}
}
Then you can re-use the functionality with a lot less code, just pass all the values you want to check to it:
var formIsValid = Utility.EnsureValuesNotEmpty(txEmail.Text,
txFirstName.Text,
txLastName.Text,
txUserEmployer.Text,
txUserPassword.Text,
txUserPassword2.Text);

Categories