[RESOLVED] Transactional programming - how do you solve it? (no DB here)
This time I'd like to ask you how do you solve such a problem that there are few steps that have to be performed in a method but it's like a transaction, this means if one step fails the function returns false.
I know three ways how to do it. With the last one I just came up today and currenty it's my favourite. Here they are:
1:
Code:
bool DoSomething()
{
bool success = false;
try
{
if(DoSomethingElse1() == false)
throw new Exception("Some description");
// ... more code here
if(DoSomethingElse2() == false)
throw new Exception("Something else2 went wrong.");
success = true;
}
catch(Exception ex)
{
// ...
}
finally
{
// ... some clean up etc.
}
return success;
}
Try/catch is a very clear way to do it, but the performance drops when the function gets called many times and it often fails.
2:
Code:
bool DoSomething()
{
if(DoSomethingElse1() == false)
{
// .. do some clean up here etc.
return false;
}
// ... more code here
if(DoSomethingElse2() == false)
{
// .. do some more clean up
return false;
}
return true;
}
this one I think is quite fast but I have to clean up on each fail so it is lots of redundant code.
3:
Code:
bool DoSomething()
{
bool success = false;
do
{
if(DoSomethingElse1() == false)
break;
// ... more code here
if(DoSomethingElse2() == false)
break;
success = true;
}
while(false);
// ... some clean up etc.
return success;
}
so, this is my latest invention ;]
what do you think about all of them? have you other ways to do this kind of transaction in your code?
Re: Transactional programming - how do you solve it? (no DB here)
Readability is more important than what you're trying to achieve here. The best possible .Net solution (in my opinion) is to use Exceptions.
1) It's known by everyone
2) It's easy to read
3) It's specific (you can specify exactly what went wrong using derived types of System.Exception)
4) It's serializable
It's just the norm. If I opened up some code and saw a bunch of early returns/breaks in a method, I'd most likely refactor it to use Exceptions instead... Go for readability and simplicity and you'll thank yourself later (as will people reading your code).
Re: Transactional programming - how do you solve it? (no DB here)
I don't like none of your options. I would sugesto something like
Code:
bool DoSomething()
{
try
{
if(DoSomethingElse1() == false) return false;
// ... more code here
if(DoSomethingElse2() == false) return false;
return true;
}
catch(Exception ex)
{
return false; // it is question if catch the exception end return false, or let it raise up the stack; maybe some expected exceptions shoudl be catched, while the remaining no
}
finally
{
// ... some clean up etc.
}
}
In your 1. you gain nothing using exceptions this way, because you don't benefit from any extra piece of information if you throw it and immediately cacht it.
Your 2. lacks the exception handling. If you don't need it, you can go this way.
Your 3. is overcomplicated, you don't need the do-while loop, just return would do the same job.
Re: Transactional programming - how do you solve it? (no DB here)
the point is that i cannot return anywhere. i often have to do a lot of cleanup and returning at the point where something went wrong makes the cleanup overcomplicated because i have to repeat the same code each time before i return false.
will the finally block be called if i retun in the try or catch part? besides, try/catch is extreamly slow in some cases and i cannot afford to use it every time.
Re: Transactional programming - how do you solve it? (no DB here)
1) try/catch is not extremely slow. It's a correct and proper solution.
2) Yes, finally will be hit no matter what
Re: Transactional programming - how do you solve it? (no DB here)
Quote:
Originally Posted by
mariocatch
1) try/catch is not extremely slow. It's a correct and proper solution.
i think you believe it's not slow, but you've never tested it or read about it, then see for your self. there is 500miliseconds difference for only 100 tests, if this is not extremly slow...
Code:
private static void TestTransactions()
{
int length = 100;
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();
for (int i1 = 0; i1 < length; ++i1)
{
TransactionMethodWithTryCatch(i1);
}
sw.Stop();
Console.WriteLine("try/catch:" + sw.ElapsedMilliseconds); // about 500 miliseconds
sw.Reset();
sw.Start();
for (int i1 = 0; i1 < length; i1++)
{
TransactionMethodWithDoLoop(i1);
}
sw.Stop();
Console.WriteLine("do/while:" + sw.ElapsedMilliseconds); // 0
}
private static bool TransactionMethodWithTryCatch(int num)
{
bool success = false;
try
{
if (true)
throw new Exception("Transaction failed.");
success = true;
}
catch (Exception ex)
{
}
return success;
}
private static bool TransactionMethodWithDoLoop(int num)
{
bool success = false;
do
{
if (true)
break;
success = true;
} while (false);
return success;
}
Re: Transactional programming - how do you solve it? (no DB here)
I use .Net to develop easy, usable, readable, re-usable, and reliable tools. Sacrificing a little speed for a more easily readable, usable solution that any developer doesn't have to strain their eyes over, is worth 500 milliseconds of execution speed IMO.
There's more people in this world than the developer of the program. Might I suggest you read up on .Net Framework Guidelines/Idioms to see where my point of view is coming from, and how it's more important than half a second to me.
Re: Transactional programming - how do you solve it? (no DB here)
i quite agree with you and i follow this principle most of the time, but there are situations where speed is very important in areas where something has to run smoothly in real time, like a CAD application so i'm trying to keep the code still readable but mutch faster and i think the do/while solution is one of the best... until someone else shows me another clean and good looking way to abbort some operation. thats why i opened this topic.
Re: Transactional programming - how do you solve it? (no DB here)
C# is not real-time platform (and Windows is not real-time operating system). If the speed is a must, you should consider using C++ instead C#.
Re: Transactional programming - how do you solve it? (no DB here)
ok, maybe your're right (i'm too lazy to check it now) but it has to run smoothly. and why should i use c++, xna is after all c# too.
i think further discussion is pointless because most of you are just too conservative. you've learnt the try/catch technique and any other idea is unacceptable even if it's hundered times faster.
edit: oops, it seems that this time i have been to conservative and didn't want to see the new way (to me) of using try/catch. sorry :]
Re: Transactional programming - how do you solve it? (no DB here)
Quote:
Originally Posted by
memeloo
ok, maybe your're right (i'm too lazy to check it now) but it has to run smoothly. and why should i use c++, xna is after all c# too.
i think further discussion is pointless because most of you are just too conservative. you've learnt the try/catch technique and any other idea is unacceptable even if it's hundered times faster.
XNA isn't that fast..:) Its only used for smaller games for a reason..
Not using try catches is just a huge fail in my eyes it has nothing to do with being conservative.. it's just good coding!
Re: Transactional programming - how do you solve it? (no DB here)
Quote:
i think you believe it's not slow, but you've never tested it or read about it, then see for your self. there is 500miliseconds difference for only 100 tests, if this is not extremly slow...
Nope, you've got your numbers wrong somewhere. I ran it locally and with 1,000,000 iterations the difference was 2seconds versus 2ms. However, this is still a bad test as you shouldn't be throwing those exceptions anyway because you could just return false (see below). Throwing exceptions is slow as you have to generate a stacktrace every time an exception is thrown and this is slow. The overhead of try/catch or try/finally is negligible.
The best solution is as already mentioned:
Code:
bool DoSomething()
{
ResourceNeedingCleanup c = new ResourceNeedingCleanup ();
try
{
if(!DoSomethingElse1())
return false;
// ... more code here
if(!DoSomethingElse2())
return false;
}
finally {
c.Cleanup ();
// ... some clean up etc. - Will always be executed. Always.
}
return true;
}
Note that there's no 'catch' here because if an exception does occur this function doesn't know how to handle it. If you can actually handle the exception properly, then add a 'catch' here with your error handling.
Quote:
i think further discussion is pointless because most of you are just too conservative. you've learnt the try/catch technique and any other idea is unacceptable even if it's hundered times faster.
No, we're not conservative. We just have a lot of experience and know what works and what doesn't. Throwing exceptions to control the flow of a function is bad and not using try/finally to clean up resources which *must* be cleaned up is also bad.
Re: Transactional programming - how do you solve it? (no DB here)
thx @Mutant_Fruit for the re-explanation and thx @boudino for the suggestion ... i like it. and sorry that i've jumped to conlusions without testing it :] oh, and of course thx for your patience :) i would say this is the ultimate solution.