Execute multiple functions, but stop when an error occured - c#

I want to use multiple functions after each other but if something went wrong in the first function the other functions shouldn't be executed. At the moment I'm using a while loop with a switch. Is there a way to skip the whole while/switch part with something else? Maybe something like an event?
while (!ErrorActive && iStep != 3)
{
switch (iStep)
{
case 0:
DoSomething(); // this can trigger ErrorActive
iStep = 1;
break;
case 1:
DoSomething2(); // this can trigger ErrorActive
iStep = 2;
break;
case 2:
DoSomething3(); // this can trigger ErrorActive
iStep = 3;
break;
}
}
the DoSomething functions have something like this:
public void DoSomething()
{
try
{
//calculate something
}
catch
{
ErrorActive = true;
}
}
Is there a way to skip the whole while/switch part and replace it with something else (like an event maybe?) or should I always keep something in between each function to check if everything is all right?

Just move the catch one level up:
// true if all steps executed, false otherwise
bool DoSteps()
{
int lastExecutedStep = 0;
try{
DoSomething();
lastExecutedStep = 1;
DoSomething1();
lastExecutedStep = 2;
DoSomething2();
lastExecutedStep = 3;
}
catch( IOException ioex )
{
// log IO Exception
}
// ... catch more expected exception types
return (lastExecutedStep == 3);
}
void DoSomething(){
// NO try/catch here
}
Even possible without stepcounter:
// true if all steps executed, false otherwise
bool DoSteps()
{
try{
DoSomething();
DoSomething1();
DoSomething2();
return true;
}
catch( IOException ioex )
{
// log IO Exception
}
// ... catch more expected exception types
return false;
}
For a more academical approach you may want to explore Chain of responsibility pattern

You can do as follows , why you use so mach code when solution is very simple.
if(!ErrorActive){
DoSomething(); // this can trigger ErrorActive
}
if(!ErrorActive){
DoSomething1(); // this can trigger ErrorActive
}
if(!ErrorActive){
DoSomething2(); // this can trigger ErrorActive
}

You can simplify the while loop to a for loop and make use of the new switch expression syntax to simplify things a bit. Also, have the functions return a success state rather than setting some shared variable:
void Run()
{
var keepRunning = true;
for(int i = 0; keepRunning; i++)
{
keepRunning = i switch
{
0 => DoSomething(),
1 => DoSomething2(),
2 => DoSomething3(),
_ => false
};
}
}
bool DoSomething()
{
try
{
return true;
}
catch
{
return false;
}
}
bool DoSomething2()
{
try
{
return true;
}
catch
{
return false;
}
}
bool DoSomething3()
{
try
{
return true;
}
catch
{
return false;
}
}

Related

Assign passed functions result to object with variable type in C#

For an integration I'm running as a service once a day, I need to assign the result of API-calls to local variables. However, those API's might at any time decide to throw a 401 error, in which case I just want to try again, up to three times.
I've got a functioning code to do that:
List<APIEntityProject> projectList = null;
private bool SetProjectList(){
const maxRetries = 3;
const RetryPause = 3000;
int retries = 0;
do
{
try
{
projectList = ProjApi.GetProject(activeWorkspace.WorkspaceCode);
}
catch (ApiException e)
{
if (e.ErrorCode == 401) // Unauthorized error (e.g. user doesn't have access to this Workspace
{
Log.Warning("Unauthorized error while fetching projects from Workspace, try {retries}",retries);
retries++;
System.Threading.Thread.Sleep(RetryPause * retries);//Waits 3 and then 6 seconds before retrying.
}
else throw;
}
} while (projectList == null || retries < maxRetries);
if (retries == maxRetries)
{
Log.Error("An error has occured while trying to retrieve affected Projects, skipped document");
errorCount++;
return false;
}
return true;
}
But unfortunately I need to replicate this Logic so often I would like to use it in a function e.g. RetryNTimes (similar to This Solution
List<APIEntityProject> projectList = null;
List<APIEntityWBS> WBSList = null;
List<APIEntitySopeItem> SIList = null;
List<APIEntityScopeAsignment> SAList = null;
List<APIEntityActivity> ActList = null;
...
RetryNTimes(projectList,ProjApi.GetProject(activeWorkspace.WorkspaceCode),3,3000,"ProjectList");
RetryNTimes(WBSList, WBSApi.GetAllWBS(activeProject.ProjectID),3,3000,"WBSList");
RetryNTimes(SIList, SIApi.GetAllScopeItems(activeProject.ProjectID),3,3000,"ScopeItemsList");
RetryNTimes(SAList, SAApi.GetAllScopeAssignments(activeProject.ProjectID),3,3000,"ScopeAssignmentsList");
RetryNTimes(ActList, ActApi.GetAllActivities(activeProject.ProjectID),3,3000,"ActivityList");
...
private bool RetryNTimes(T object, Func<T> func, int times, int WaitInterval, string etext){
do
{
try
{
object = func();
}
catch (ApiException e)
{
if (e.ErrorCode == 401)
{
retries++;
Log.Warning("Unauthorized error while fetching {APIErrorSubject}, try {retries}",eText,retries);
System.Threading.Thread.Sleep(RetryPause * retries);//Waits 3 and then 6 seconds before retrying.
}
else throw;
}
} while (object == null || retries < maxRetries);
if (retries == maxRetries)
{
Log.Error("An error has occured while trying to retrieve {APIErrorSubject}, skipped document",eText);
errorCount++;
return false;
}
return true;
}
I've also read through typedef and function pointers but I'm not sure if it's possible to do with variable types.
Any Ideas?
That article refers to C language. In C# you can use delegates. Here's a link to start you off.
Based on the idea of asawyer and by looking through some other examples of delegates I've been able to make it work.
static T2 TryNTimes<T1,T2>(Func<T1,T2> func,T1 obj, int times, int WaitInterval)
{
while (times > 0)
{
try
{
T2 result = func.Invoke(obj);
return result;
}
catch (Exception e)
{
if (--times <= 0)
throw;
System.Threading.Thread.Sleep(WaitInterval * times);
}
}
return default;
}
Now I need only 2 steps in my main function
activeWorkspace = TryNTimes(WrkApi.WorkspaceCodeWorkspaceCodeFindByName17, ServiceSettings.sqlConnection.Workspace, 3, 3000)[0];
ProjectList = TryNTimes(WrkApi.GetProjectsByWorkspaceCode, activeWorkspace.code, 3, 3000);
The first one can still generate an error as the default List is empty and you can't take 0th element then. But I guess I can find another way around that issue.

Attempt and retry

How can I formalize this to be more generic, where I can specify an X exceptions to throw and X exceptions to try again all while improving the code readability.
private const int RetrySegmentCount = 3;
private const int SecondsBetweenRetry = 30;
var retryCounter = 0;
while (true)
{
try
{
ExecuteProcessThatMayThrow();
break;
}
catch (NotSupportedException) // Do no retry if this is thrown
{
throw;
}
catch (Exception)
{
if (retryCounter < RetrySegmentCount)
{
retryCounter++;
Thread.Sleep(SecondsBetweenRetry * 1000);
}
else
{
throw;
}
}
}
An ideal syntax in puesdocode might be
Repeat(3, 30, [NotSupportedException], [Exception]) => ExecuteProcessThatMayThrow();
Repeat(3, 30) => ExecuteProcessThatMayThrow(); // This will repeat on all
Repeat(3, 30, [NotSupportedException, VeryBadException], [RetryableException]) => ExecuteProcessThatMayThrow();
You can create a reusable method that has multiple result depending on the error type. Here a small modified version of what i use
This method handles the different conditions and retry
public static bool TryExecute(Action action, int retry, int secondBeforeRetry, List<Type> notSupportedExceptions, List<Type> veryBadExceptions, List<Type> retryableExceptions)
{
var success = false;
// keep trying to run the action
for (int i = 0; i < retry; i++)
{
try
{
// run action
action.Invoke();
// if it reached here it was successful
success = true;
// break the loop
break;
}
catch (Exception ex)
{
// if the exception is not retryable
if (!retryableExceptions.Contains(ex.GetType()))
{
// if its a not supported exception
if (notSupportedExceptions.Contains(ex.GetType()))
{
throw new Exception("No supported");
}
else if (veryBadExceptions.Contains(ex.GetType()))
{
throw new Exception("Very bad");
}
}
else
{
System.Threading.Thread.Sleep(secondBeforeRetry * 1000);
}
}
}
return success;
}
To call this method it before very easy as they can all be easily change to optional parameters. here is and example :
// sample action that force an error to be thrown
var a = new Action(() =>
{
var test = "";
var test2 = test[3]; // throw out of range exception
});
try
{
var success = TryExecute(a, 5, 30, new List<Type>() { typeof(IndexOutOfRangeException) }, new List<Type>(), new List<Type>());
}
catch (Exception ex)
{
// handle whatever you want
}

using do-while and skipping the next lines

I want to run a method _doing() that loops infinitely until a shutdownEvent is triggered. This is basically executed/started on a new Thread(). But I need not _doSomething if it is true somewhere. what should i do after if (_doSomething)? Code snippet below. Thanks.
private HighResolutionLapseTimer _lapseTimer;
private int _timeToNext
{
get
{
int lapseTime = _lapseTimer.LapseTime();
int next = DO_PERIOD - lapseTime;
if (next > 0)
{
return next;
}
else
{
return 1;
}
}
}
int DO_PERIOD = 60000;
private void _doing()
{
int _nextDoing = DO_PERIOD;
Thread.Sleep(_nextDoing);
do
{
LogInfo("Starting _doing");
lock (this)
{
if (_doSomething)
{
// skip this time because we are currently archiving
}
_doSomething = true;
}
try
{
_lapseTimer.Start();
DoSomethingHere(); //takes long processing
}
catch (Exception ex)
{
LogException(ex);
}
finally
{
lock (this)
{
_nextDoing = (int)_timeToNext;
_doSomething = false;
}
}
} while (!shutdownWaitHandle.WaitOne(_nextDoing, false));
LogInfo("Stopping _doing");
}
You could use the continue; statement.
Ohw! I just realized that a do-while is similar to a while. And to 'skip' the execution, you just have to use the continue; if the if statement is true

How to pause the "for" loop until getting response

I am using a for loop for making Calls for a list of numbers.
I want to take the first number from the list and make a call and to wait for the response and then proceed to the next number in the list.
I have used AutoResetEvent to do this.But it is not working.
for (int k = 0; k < list_Items.Count; k++) {
Number_To_Call = "9" + list_Items[k].ToString();
phoneCall.Start();
waitingToPickUp.Set(); //AutoReset Event
Thread.Sleep();
waitingToPickUp.WaitOne();
string detector = VoiceDetected;
if (detector == "Machine") {
//code
} else if (detector == "Human") {
//code
} else {
//code
}
}
Code for getting response form the call
void phoneCall_CallStateChanged(object sender, VoIPEventArgs<CallState> e)
{
if (e.Item.IsInCall())
{
phoneCallAudioReceiver.AttachToCall(phoneCall);
phoneCallAudioSender.AttachToCall(phoneCall);
manchineDetector.Start();
waitingToPickUp.Set();
string str = VoiceDetected;
}
else if (e.Item.IsCallEnded())
{
phoneCallAudioReceiver.Detach();
phoneCallAudioSender.Detach();
manchineDetector.Stop();
phoneCall = null;
//Number_To_Call = string.Empty;
InvokeOnGUIThread(() =>
{
Number_To_Call = string.Empty;
});
}
}
Code for Detecting Machine or Human
void manchineDetector_DetectionCompleted(object sender, VoIPEventArgs<AnswerMachineDetectionResult> e)
{
try
{
string VoiceDetected = e.Item.ToString();
}
catch (Exception ex)
{
}
}
Set and immediately WaitOne makes no sense - wait will not need to wait for anything and immediately continue.
Most likely should be reset-call-wait:
waitingToPickUp.Reset();
phoneCall.Start();
waitingToPickUp.WaitOne();

How to leave a while loop inside a foreach loop

I have a while loop and inside this while loop I have a foreach loop.
Here I learned how to skip the currently interaction on a loop by using Continue; Return; Break. But I need to leave the while loop when i'm inside the foreach loop is that possible ?
I'm on a interaction inside of the foreach loop and I want to leave the foreach and go to the next interaction of the while. How May I do that ?
Like so:
while (!reader.EndOfStream) //COMEÇO DO WHILE
{
///Some Codes
foreach(string s in checkedlistbox1.items)
{
switch(s)
{
case "1":
if( 1 > 0)
{
///HERE I WANT TO GO TO THE NEXT INTERACTION OF THE WHILE
///When I use CONTINUE; HERE, I GO TO THE NEXT INTERACTION OF THE FOREACH. BUT I NEED TO GO TO THE NEXT OF THE WHILE.
}
}
}
}
Here's what I want do to:
I'm reading a file.txt line by line, and writing a new one with these values and some others things... Some of these values may be required (seted by user), if a required fieldis empty, so I do nothing, and go to the next while interaction...
You need to break from the switch and then from the foreach, whilst having a variable set.
You can then check that variable to see whether you should continue to the next while iteration.
Do it like this:
while (!reader.EndOfStream)
{
// Some Codes
bool skipToNext = false;
foreach (string s in checkedlistbox1.items)
{
switch (s)
{
case "1":
if (1 > 0)
{
skipToNext = true;
break;
}
}
if (skipToNext) break;
}
// in the case of there being more code, you can now use continue
if (skipToNext) continue;
// more code
}
Example of this flow working
var list = new List<string> { "0", "1", "2" };
int a = 0, b = 2;
while (a++ < b)
{
// Some Codes
bool skipToNext = false;
foreach (string s in list)
{
Debug.WriteLine("{0} - {1}", a, s);
switch (s)
{
case "1":
if (1 > 0)
{
Debug.WriteLine("Skipping switch...");
skipToNext = true;
break;
}
}
if (skipToNext)
{
Debug.WriteLine("Skipping foreach...");
break;
}
}
// in the case of there being more code, you can now use continue
if (skipToNext)
{
Debug.WriteLine("Skipping to next while...");
continue;
}
// more code
}
Outputs:
1 - 0
1 - 1
Skipping switch...
Skipping foreach...
Skipping to next while...
2 - 0
2 - 1
Skipping switch...
Skipping foreach...
Skipping to next while...
bool dobreak = false;
while (!reader.EndOfStream && !dobreak ) //COMEÇO DO WHILE
{
///Some Codes
foreach(string s in checkedlistbox1.items)
{
switch(s)
{
case "1":
if( 1 > 0)
{
dobreak = true;
break;
}
}
}
}
1) Yes, it is possible. It works.
foreach() {
while() {
break; //leave the while
}
//... and continues from here
}
2) Each while will end before the next foreach iteration, so the second question does not make much sense... Unless you mean starting the next while inside the foreach, in which case.. yes!
foreach() {
while() {
...
}
break; //will go to the next foreach iteration, i.e. starts a new while
}
As for your code sample, a break in the point you mentioned in the comment will do what you need (exit the foreach, going naturally to the next while iteration).
EDIT: after you posted the example, it appears that your problem is not in the interaction between while and foreach, but in the switch: break is used as a keyword for both "go to the next iteration in the loop" and "finish this case and the switch".
The break will be seen by the compiler as a 'switch break'. You need to mimic the behavior by yourself:
foreach(string s in checkedlistbox1.items)
{
bool dobreak = false;
switch(s)
{
case "1":
if( 1 > 0)
{
dobreak = true;
}
break; // exit the case
}
if (dobreak)
break; // exits the for (it is a 'foreach-break')
}
Not tested, but these are a few possible ways which should answer your question:
bool continueLooping = true;
string[] array = { "1", "2" };
while (continueLooping)
{
foreach (string x in array)
{
// break out of foreach loop AND while loop
if (x == "1")
{
continueLooping = false;
break;
}
// go to next iteration of foreach loop
if (x == "2")
{
continue;
}
// break out of foreach loop and continue while loop
if (x == "3")
{
break;
}
}
}
You need to break from your foreach and the while so something like this:
bool abort = false;
while (!reader.EndOfStream && !abort) //COMEÇO DO WHILE
{
///Some Codes
foreach(string s in checkedlistbox1.items)
{
switch(s)
{
case "1":
if( 1 > 0)
{
///HERE I WANT TO LEAVE TO THE NEXT INTERACTION OF THE WHILE
abort = true;
break;
}
}
if (abort)
{
break;
}
}
}
I'm beginner in C#, but I suggest that you add another condition to the while loop that you can set yourself. Then, change it whenever is needed. For example:
MyKeyCondition = 0;
while(MainCondition||MyKeyCondition){
if(MyCheckCondition){
MyKeyCondition = 1;
}
}
Now, if you change your key condition, you can handle the while loop even if the main condition is not satisfied.
May try goto;
while (true)
{
foreach(string s in checkedlistbox1.items)
{
switch(s)
{
case "1":
if( 1 > 0)
{
goto WhileOut;
}
}
}
}
WhileOut:
// some code
Often nested loops are an indication that a method should be split up.
In this example, it could make the code clearer. If you place the inner for loop into a separate method, you can make that method return a bool. Return true if you want the outer while loop to continue iterating; otherwise, return false if you want it to exit the while loop.
It would look something like this:
private void doSomething(StreamReader reader)
{
while (!reader.EndOfStream)
{
// Some Codes...
if (!processNextItem(reader))
{
break;
}
}
}
// Returns true if the caller should continue its while loop; false if it should exit it.
private bool processNextItem(StreamReader reader)
{
foreach (string s in checkedlistbox1.items)
{
switch (s)
{
case "1":
if (1 > 0)
{
return false; // Return false to exit while loop.
}
}
}
return true; // Return true to continue while loop.
}
I'm not sure what you'd need to pass to processNextItem(); I've just passed reader as an example.

Categories