pushkin
pushkin

Reputation: 10199

C# compiler doesn't complain when not all code paths return

I had a function that was returning in a using block:

int f() {
  using (...) {
     ...
     return someVar;
  }
}

I just noticed this and moved the return outside of the using block to the outermost function scope, because I felt that that's where the return should be.

But I'm confused why the compiler wasn't complaining that not all code paths were returning. Is it simply because if it failed to initialize a resource, we would get a crash, so it doesn't matter?

Take this example:

class MainClass {
  public static void Main (string[] args) {
    f();
  }

  public static int f() {
    using(A a = new A()) {
      return 1;
    }
  }
}

class A : IDisposable{
  public void Dispose() { }
}

The compiler doesn't care that we only return in using. However, I thought using statements were basically syntactic sugar for try/catch

If we replace

using(A a = new A()) {
  return 1;
}

with

A a = new A();;
try  {
  return 1;
}

catch (Exception e) { }
finally {
  if (a != null) {
    ((IDisposable) a).Dispose();
  }
}

Indeed the compiler complains:

error CS0161: `MainClass.f()': not all code paths return a value

Why doesn't it complain in the other case?

Is it simply what I said above? If it fails to initialize a resource, we would get a crash, so the compiler decides it doesn't matter.

Upvotes: 3

Views: 191

Answers (2)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476574

Actually the:

using(var objectName = <init-expression>) {
    //...
}

Is more or less equivalent to:

objectName = <init-expression>;
try {
    //...
} finally {
    objectName.Dispose();
}

So it is a try-finally-block: if something goes wrong during the execution, the exception will be thrown out of the method (mostly after the finally part is done).

A try-finally however does not create an alternative code path: if the try-part returns something or throws an error, it will first execute the finally-part, but then either throw the exception or return what is ought to be returned.

Upvotes: 5

Matti Price
Matti Price

Reputation: 3551

Essentially, yes. The using statement will throw it's exception up the stack. Catching the exception and using a finally block that doesn't return means that the method both doesn't throw an exception and doesn't return.

The compiler also doesn't complain about methods like the following

public int SuperCool(){
   throw new NotImplementedException("bummer");
}

To expand a little since I missed an edit with the catch block not being there originally:

Having the catch block "eats" the exception causing it to not move further up the stack and the compiler to notice that there is no path that returns a value or an exception.

Upvotes: 4

Related Questions