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.
Related
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();
}
I am somewhat of a beginner programmer. What I am trying to do here is to check that if there is a time, if it is selected, and if that time is equivalent to the other. If that is all true then I want to skip the block of code under it. Here is the code example:
if (currentGVR.Round_Start_Time)
{
if (tr.StartLunchDateTime != null && currentGVR.Round_Start_Lunch && roundedStart == roundedStartL)
// skip
else
{
key = tr.TransactionID;
TransactionRecords[key]["Start_DateTime"] = roundedStart;
}
}
I thought about using an OR operator, but I can see where an error would occur if there was no time to compare to. Using the AND operator avoids this dilemma here.
So the overall question is, is it proper coding to negate all of the conditions to get the correct result, e.g. if (!( cond's )), and also, would this be the best way to check if there is a value to compare with before actually comparing it in C# and otherwise? The times can be null (or do not exist) in some records. Any recommendations?
I'd negate all those conditions and switch the && to || so it's more quickly evident under what conditions the code will (or will not) execute.
Plus (in my experience), you don't typically see an empty if block with all the code under the else.
if (tr.StartLunchDateTime == null || !currentGVR.Round_Start_Lunch || roundedStart != roundedStartL)
{
key = tr.TransactionID;
TransactionRecords[key]["Start_DateTime"] = roundedStart;
}
The statement
if (tr.StartLunchDateTime != null && currentGVR.Round_Start_Lunch && roundedStart == roundedStartL){
// skip
}
else
{
key = tr.TransactionID;
TransactionRecords[key]["Start_DateTime"] = roundedStart;
}
is equivalent to
if (!(tr.StartLunchDateTime != null && currentGVR.Round_Start_Lunch && roundedStart == roundedStartL))
{
key = tr.TransactionID;
TransactionRecords[key]["Start_DateTime"] = roundedStart;
}
else {
// skip
}
This can be further simplified because
!(tr.StartLunchDateTime != null &&
currentGVR.Round_Start_Lunch &&
roundedStart == roundedStartL)
Is the same as
(!(tr.StartLunchDateTime != null) ||
!(currentGVR.Round_Start_Lunch) ||
!(roundedStart == roundedStartL))
or
(tr.StartLunchDateTime == null ||
!currentGVR.Round_Start_Lunch ||
roundedStart != roundedStartL)
See DeMorgan's Laws.
if (someLongCondition)
{ }
else
{
doStuff();
}
is equivalent to this:
if (!someLongCondition)
{
doStuff();
}
So yeah, you can just negate your whole condition:
if (!(tr.StartLunchDateTime != null && currentGVR.Round_Start_Lunch && roundedStart == roundedStartL))
{ … }
But you can also pull the negation in (applying De Morgan's laws) and write it like this:
if (tr.StartLunchDateTime == null || !currentGVR.Round_Start_Lunch || roundedStart != roundedStartL)
{ … }
All these are equivalent so choose whatever makes the condition more clear (actually, consider storing it in a separate variable which you give a descriptive name).
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
...
I'm tracing some code, and a list is sent to the below, to remove certain items from the list.
Is this the proper way to use the goto? Is it even necessary? Wouldn't you just edit the second if to be an else if, and continue processing the list without the need for a goto? (This is the first time I've ever seen goto outside of BASIC.)
public static IList<ClientEntity> FilterNoNeedSendBackToClients(IList<ClientEntity> src)
{
if (src == null)
return null;
checkAB01:
for (int i = 0; i < src.Count; i++)
{
ClientEntity info = src[i];
if (info.ProductNumber != null && info.ProductNumber.ToLower().Trim().Length > 0
&&
info.CancelledByReliantSyncAB01 != null && info.CancelledByReliantSyncAB01.Value == true
)
{
src.Remove(info);
goto checkAB01;
}
if ((info.PartnerContract == null || info.PartnerContract.Trim().Length == 0)
&&
(info.ProductNumber == null || info.ProductNumber.Trim().Length == 0))
{
src.Remove(info);
goto checkAB01;
}
}
return src;
}
How about LINQ?
public static IList<ClientEntity> FilterNoNeedSendBackToClients(IList<ClientEntity> src)
{
if (src == null)
return null;
return (from info in src.AsEnumerable<ClientEntity>()
where !(!String.IsNullOrWhiteSpace(info.ProductNumber) &&
info.CancelledByReliantSyncAB01 == (bool?)true)
where !(String.IsNullOrWhitespace(info.PartnerContract) &&
String.IsNullOrWhiteSpace(info.ProductNumber))
select info).ToList();
}
No, this isn't a proper way to use goto, it's just a substitute for someone who doesn't know how to properly remove items from a list. (Iterate backwards to prevent skipping elements)
public static IList<ClientEntity> FilterNoNeedSendBackToClients(IList<ClientEntity> src)
{
if (src == null)
return null;
for (int i = src.Count - 1; i >= 0; i--)
{
ClientEntity info = src[i];
if (info.ProductNumber != null && info.ProductNumber.ToLower().Trim().Length > 0
&&
info.CancelledByReliantSyncAB01 != null && info.CancelledByReliantSyncAB01.Value == true
)
{
src.RemoveAt(i);
}
if ((info.PartnerContract == null || info.PartnerContract.Trim().Length == 0)
&&
(info.ProductNumber == null || info.ProductNumber.Trim().Length == 0))
{
src.RemoveAt(i);
}
}
return src;
}
I think whoever wrote this code did not know how to remove items from the iterated collection. The reason for those gotos was that once an item is deleted, the collection becomes smaller which can cause iteration errors.
A better day to do this is to do a reverse for loop. This way you do not have to reiterate through the whole list after a deletion. The code below will work just fine.
for (int i = src.Count - 1; i >= 0; i--) {
src.Remove(src[i]);
}
As others have said, this is a poor usage of goto (which should rarely, if ever, be used)
Overall though, the implementation is terribly inefficient. Looks like it loops through from 0..N until it removes something, then starts again from 0..N (now 1 less) until it removes something, and so on. Even worse, the Remove call again goes from 0..N looking for that item to remove.
If it finds nothing to remove, it returns. Better to just do a reverse for loop removing entries with RemoveAt then returning.
public static IList<ClientEntity> FilterNoNeedSendBackToClients(IList<ClientEntity> src)
{
if (src == null)
return null;
for (int i = src.Count - 1; i >= 0; i--)
{
ClientEntity info = src[i];
if (info.ProductNumber != null && info.ProductNumber.ToLower().Trim().Length > 0
&&
info.CancelledByReliantSyncAB01 != null && info.CancelledByReliantSyncAB01.Value == true)
{
src.RemoveAt(i);
}
else if ((info.PartnerContract == null || info.PartnerContract.Trim().Length == 0)
&&
(info.ProductNumber == null || info.ProductNumber.Trim().Length == 0))
{
src.RemoveAt(i);
}
}
return src;
}
Also, I added an elseif there: seems dangerous to do another if check that could potentially be true and try to re-remove the same item (especially after changing the indices).
EDIT: If we're talking about readable usable code, I'd move out the checks to a separate method anyway:
public static IList<ClientEntity> FilterNoNeedSendBackToClients(IList<ClientEntity> src)
{
if (src == null)
return null;
for (int i = src.Count - 1; i >= 0; i--)
{
if (ShouldClientNotSendBack(src[i]))
src.RemoveAt(i);
}
return src;
}
private static bool ShouldClientNotSendBack(ClientEntity info)
{
if (!String.IsNullOrWhiteSpace(info.ProductNumber) && info.CancelledByReliantSyncAB01 == true)
{
return true;
}
if (!String.IsNullOrWhiteSpace(info.PartnerContract))
{
return true;
}
return false;
}
Might even consider tweaking that ShouldClientNotSendBack method and/or name (perhaps even move the two sets of if checks to individual methods with clear names), but this I think is a significant improvement overall.
EDITx2: In fact, I would strongly consider usage of the method. The method is clearly returning an IList<ClientEntity> while taking in an input collection, which typically communicates to developers that this is creating a new list when in fact it's actually mutating the existing list and returning the same list instance. Either have it return a new list (thus you should change the loop code to add to a new list instead of removing from the existing) or remove the return type so it's more apparent that it's mutating the passed list argument.
As I stated in my comment, there is no "proper" way to use goto in C#. The keyword is, by its very definition, a kludge; it's a throwback to C/C++, which included it as a "just in case", should a developer want to translate line-by-line a program in ASM or BASIC or other language without defined code blocks, which hide the "jumps" used to get into and between them. Any algorithm that uses it can be refactored to not have to do so, and the resulting algorithm will be more readable.
In this case:
public static IList<ClientEntity> FilterNoNeedSendBackToClients(IList<ClientEntity> src)
{
if (src == null)
return null;
for (int i = src.Count-1; i>=0; i--)
{
ClientEntity info = src[i];
if (info.ProductNumber != null && info.ProductNumber.ToLower().Trim().Length > 0
&&
info.CancelledByReliantSyncAB01 != null && info.CancelledByReliantSyncAB01.Value == true
)
{
src.Remove(info);
continue;
}
if ((info.PartnerContract == null || info.PartnerContract.Trim().Length == 0)
&&
(info.ProductNumber == null || info.ProductNumber.Trim().Length == 0))
{
src.Remove(info);
continue;
}
}
return src;
}
As Chris Sinclair's answer shows, because of the "either/or" implicit structure of the conditionals in the loop, the continue statements aren't necessary, but I like them because they show the coder that nothing from that point to the end of the loop will be run, without them having to read the rest of it to determine that.
If you wanted to, you could run through the list front to back, and if an item is removed, decrement i before continuing so that the loop will maintain its current position in the list. Some may say not to do it this way because the manipulation of the counter makes it harder to understand the algorithm (and because determining the count of the list on each iteration is slightly slower), but it's still far better in both performance and readability than the goto.
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);