views:

155

answers:

6

I have to make a sequence of method calls in C# such that, if one of them fails, the subsequent methods should not be called. In short, the set of calls should be made atomic. How do I achieve this in C#?

A: 

Preferably, get them to throw an exception when they fail and write your call sequence in a try/catch block.

If you can't do that for some reason, get them to return true on success and use &&:

if (a() && b() && c())
{
    ....

(That's not "atomic" in the true sense of the word, but I don't think you're asking for true atomicity.)

RichieHindle
But then they would all have to be a defined as a `bool` method, wouldn't they? then they couldn't return anything else, and thus the exception route is better.
Richard J. Ross III
@Richard J. Ross III: Absolutely the exception route is better.
RichieHindle
agreed..but i remember having read somewhere that C# provides something specifically to handle such a situation..looking for that way
Aadith
A: 

If you are not catching exceptions, then if you throw an exception, all other methods called abort until a try block is found. So simply throw an exception where you need to have the atomic calls end (e.g. when it fails) and then catch it when you need to return to normal rutine.

Richard J. Ross III
agreed..but i remember having read somewhere that C# provides something specifically to handle such a situation..looking for that way
Aadith
@Aadith. Handle what situation? When people demonstrate how to supply atomicity you complain that this isn't what you want, and when people demonstrate how to just abort early you talk of "such a situation".
Jon Hanna
+2  A: 

TransactionScope might be what you need see here

void RootMethod()
{
     using(TransactionScope scope = new TransactionScope())
     {
          /* Perform transactional work here */
          SomeMethod();
          SomeMethod2();
          SomeMethod3();
          scope.Complete();
     }
}
Yoann
Note that he's specifying that rollback is *not* required - he wants to stop calling methods if one fails. In fact, I think the word "atomic" is used here incorrectly.
Philip Rieck
+4  A: 

I think you're confusing the word "atomic" with something else. Atomic is when an operation cannot be interrupted and is usually done in multi threaded scenarios to protect shared resources.

What you want is normal control flow logic and the solution depends on what your methods looks like.

One solution could be to have them return a boolean indicating whether or not it succeeded:

bool success = false;

success = MethodA();
if (!success)
  return;
success = MethodB();
if (!success)
  return;

// or even like this as suggested in another answer
if (MethodA() &&
    MethodB() &&
    MethodC())
{
  Console.WriteLine("All succeeded");
}

You could also use exceptions and wrap all your method calls inside a try-catch block. If one of them fails (throws an exception), your catch block will execute and nothing after that method call in the try-block will get a chance to run.

try
{
  MethodA();
  MethodB();
  MethodC();
}
catch (MyMethodFailedException)
{
  // do something clever
}

If you need rollback functionality, you have to get into transactions but that's a whole bigger topic.

Isak Savo
A: 

Here is a rough example of emulating a move operation with compensation if things go wrong. With exceptions thrown from your device copy methods on failure

string source = @"C:\file.txt", dest = @"D:\file.txt";

bool fileCopied = false;
try
{
    DeviceCopy(source, dest);
    fileCopied = true;
    DeviceDelete(source);
}
catch
{
    if (fileCopied)
    {
        DeviceDelete(dest);
    }
    throw;
}

Or with error codes e.g. could be bool for failed or check an integer

if (DeviceCopy(source, dest))
{
    if (!DeviceDelete(source))
    {
        if (!DeviceDelete(dest))
        {
            throw new IOException("Oh noes");
        }
    }
}
Bear Monkey
A: 

Are you thinking of multicast delegates? Something like:

delegate void SomeFunc(int x);

void foo() {
    SomeFunc funcSeq = new SomeFunc(func1);
    funcSeq += new SomeFunc(func2);
    funcSeq(100);   // Invokes func1 and func2
}

void func1(int foo) {
    // Use foo
}

void func2(int bar) {
    // Use bar
}

This won't work, because the order that the functions is called in is undefined. Plus, since your delegate functions can't return a value, there's no way to indicate that they failed, except by throwing an exception. And if you're going to throw an exception, you're better off using one of the earlier suggestions, as then your order of execution is defined (and your methods don't all have to have the same signature).

TMN