-6

I have many scenarios during my development where I want to do something such as

try 
{ 
    long presult = EvalInner(eqtn,new Tuple<int, int>(++begidx,curidx-1),eqtnArgs); 
}
catch ( Exception e ) 
{ 
    throw e; 
}
result = evalNewResult(result,lastop,presult,ref negateNextNum,ref negateNextOp); 
// ...
return presult;

but then my compiler flags the presult on the line

result = evalNewResult(result,lastop,presult,ref negateNextNum,ref negateNextOp); 

saying

The name 'presult' does not exist in the current context

If it were smart, it would understand that presult is either initialized in the try block, or the procedure is exited before presult is ever used.

Possible workarounds (none of them good):

  1. Declare long presult; right before the try statement. This makes the compiler mad because it wrongly thinks there's a possibility of returning an unintialized variable.
  2. Initialize it with long presult = default(long). This works, but it's bad practice because someone reading the code doesn't know whether intializing it to the default value is to work around the problem described in 1. or is because the value presult because set to the default long has some real meaning in the context of the program.
  3. Initialize it with long? presult = null. This is semantically better because it's clear that it means "presult is meant to have no value at this point" whereas in 2. the reader has to figure out that presult has a meaningless value. The problem here is that, not only does it take extra memory to nullify a value, but I then have to change the function EvalInner to return a long? and this results in a chain of needing to change many more longs to long?s and my program ends up splattered with nullified variables; it's a complete mess of question marks haha.

Anyways, how should I be handling a case like this?

Tim Barrass
  • 4,813
  • 2
  • 29
  • 55
  • 7
    FYI You should never re-throw an exception as you lose the stack trace. Instead release it by changing `throw e;` to `throw;`. – juharr Oct 21 '15 at 15:20
  • 3
    Option 1 does not make the compiler mad. The compiler is right. You are returning an uninitialized variable. Should it be `return result` instead of `return presult`? – Thomas Weller Oct 21 '15 at 15:22
  • 1
    You are using a variable, that is assigned in a try/catch block, outside that block. You'll want to move the whole code into the try block. Like showed in first answer – Kevin Avignon Oct 21 '15 at 15:23
  • 2
    The real answer to this is only going to come from the C# language/compiler team. I suspect that they _could_ allow it, but it would need compiler changes to support it. – James Thorpe Oct 21 '15 at 15:26
  • 1) Why is the a try block that just throws? 2) `result = evalNewResult(result,lastop,presult,ref negateNextNum,ref negateNextOp);` doesn't look like it makes sense to do after there has been an exception, why is it outside the try block? – Nathan Cooper Oct 21 '15 at 15:26
  • I'm with @JamesThorpe. This was likely a case of is it worth it and how much effort is required to do it. – juharr Oct 21 '15 at 15:30
  • Also I would expect any experienced C# programmer to understand what is going on with option 2. But if you're really worried about it then add a comment to make it clear. – juharr Oct 21 '15 at 15:32
  • @juharr Can you provide with a link to documentation pertaining to what you're saying about rethrows (I use them all the time lol)? – Failed Computer Science 101 Oct 21 '15 at 15:34
  • 1
    @FailedComputerScience101 "throw vs throw ex" is covered in detail in [this question](http://stackoverflow.com/questions/730250/is-there-a-difference-between-throw-and-throw-ex) – James Thorpe Oct 21 '15 at 15:34
  • Dammit. There are live applications in which I recall using rethrows – Failed Computer Science 101 Oct 21 '15 at 15:39

6 Answers6

2

I'll go over your points one by one:

  1. Declare long presult; right before the try statement. This makes the compiler mad because it wrongly thinks there's a possibility of returning an unintialized variable.

Actually, the compiler correctly determines that there is the possibility of returning an uninitialized variable. Since the variable is only set if the function on the right hand side succeeds, and since you have it in a try..catch block then there is the possibility that the function may throw and not return, therefore not initializing the variable. What the compiler is not smart enough to see is that you are catching the top level exception and throwing (in a bad way, losing the stack trace) and it should not reach the return. However there are ways to get around that (mostly during debug by dragging the execution cursor).

  1. Initialize it with long presult = default(long). This works, but it's bad practice because someone reading the code doesn't know whether intializing it to the default value is to work around the problem described in 1. or is because the value presult because set to the default long has some real meaning in the context of the program.

Since value types like long, int, short etc must have a value, this is not bad practice. If you want to represent them as not having a value, use the nullable versions of those types (i.e. long? presult = null).

  1. Initialize it with long? presult = null. This is semantically better because it's clear that it means "presult is meant to have no value at this point" whereas in 2. the reader has to figure out that presult has a meaningless value. The problem here is that, not only does it take extra memory to nullify a value, but I then have to change the function EvalInner to return a long? and this results in a chain of needing to change many more longs to long?s and my program ends up splattered with nullified variables; it's a complete mess of question marks haha.

Again, the function must return a value that is a valid long, so if you want to return something that can easily be identified as an incorrect value, then return the nullable version, otherwise you have to return a valid value. Only float and double have NaN members...

Another option would be some kind of TryXXX method, where the return value is a boolean and you use an out long as a parameter to store the result.

Ron Beyer
  • 11,003
  • 1
  • 19
  • 37
0

How about:

try 
{ 
    long presult = EvalInner(eqtn,new Tuple<int, int>(++begidx,curidx-1),eqtnArgs); 
    result = evalNewResult(result,lastop,presult,ref negateNextNum,ref negateNextOp); 
    // ...
    return presult;
}
catch ( Exception e ) 
{ 
    //Do some useful logging
    throw; //Don't lose stacktrace!
}
Mister Epic
  • 16,295
  • 13
  • 76
  • 147
  • This is different because this will now catch potential exceptions from the `evalNewResult` call as well. – juharr Oct 21 '15 at 15:23
  • @DanielKelley Yes, this is an absurdity, but was only to indicate that block-level scoping allows you to init the variable within the try block. I added a comment to indicate that he should perhaps do some logging to make it slightly less absurd :) – Mister Epic Oct 21 '15 at 15:27
0

I don't understand you problem. The compiler can't know the value of presult when you call evalNewResult that's why you need to declare it outside the try block. It's a general rule of scopes in C# and a lot of other languages.

The solution is to declare and initialize it before the try block. The question is "what value should presult have in case an exception occurs". The compiler can't ask this question himslef.

jaudo
  • 2,022
  • 4
  • 28
  • 48
  • I realize I can intialize it before the try block (options 2. or 3. that I mentioned), but that doesn't seem ideal in a situation where the variable's value is supposed to have no meaning at that point in the code – Failed Computer Science 101 Oct 21 '15 at 15:32
  • Then, the call to evalNewResult should also be in the try block. – jaudo Oct 21 '15 at 15:35
0

Please check this link for more enlightment

Compilers are in the business of generating code which manages the storage of the data manipulated by that program. There are lots of different ways of generating code to manage memory, but over time two basic techniques have become entrenched.

The first is to have some sort of "long lived" storage area where the "lifetime" of each byte in the storage -- that is, the period of time when it is validly associated with some program variable -- cannot be easily predicted ahead of time. The compiler generates calls into a "heap manager" that knows how to dynamically allocate storage when it is needed and reclaim it when it is no longer needed.

The second is to have some sort of "short lived" storage area where the lifetime of each byte in the storage is well known, and, in particular, lifetimes of storages follow a "nesting" pattern. That is, the allocation of the longest-lived of the short-lived variables strictly overlaps the allocations of shorter-lived variables that come after it.

Local variables follow the latter pattern; when a method is entered, its local variables come alive. When that method calls another method, the new method's local variables come alive. They'll be dead before the first method's local variables are dead. The relative order of the beginnings and endings of lifetimes of storages associated with local variables can be worked out ahead of time.

For this reason, local variables are usually generated as storage on a "stack" data structure, because a stack has the property that the first thing pushed on it is going to be the last thing popped off.

So overall local variable are are short lived and usually stored in stack, why stack coz its efficient than others. Link for more info why stack

Community
  • 1
  • 1
Sandip Bantawa
  • 2,822
  • 4
  • 31
  • 47
0

Why doesn't my C# compiler (Visual Studio) let me do this with a try block?

That is because braces define a scope. From Variable and Method Scope in Microsoft .NET:

If you declare a variable within a block construct such as an If statement, that variable's scope is only until the end of the block. The lifetime is until the procedure ends.

how should I be handling a case like this?

Go for option 1.

This makes the compiler mad because it wrongly thinks there's a possibility of returning an unintialized variable

Option 1 does not make the compiler mad. The compiler is always right :-)

I created the following SSCCE and it absolutely works:

using System;

namespace app1
{
    class Program
    {
        static void Main(string[] args)
        {
            Presult();
        }

        private static long Presult()
        {
            long presult;
            try
            {
                object eqtn = null;
                char begidx = '\0';
                int curidx = 0;
                object eqtnArgs = null;
                presult = EvalInner(eqtn, new Tuple<int, int>(++begidx, curidx - 1), eqtnArgs);
            }
            catch (Exception e)
            {
                throw e;
            }
            int result = 0;
            object lastop = null;
            object negateNextNum = null;
            object negateNextOp = null;
            result = evalNewResult(result, lastop, presult, ref negateNextNum, ref negateNextOp);
            // ...
            return presult;
        }

        private static int evalNewResult(int result, object lastop, long presult, ref object negateNextNum, ref object negateNextOp)
        {
            return 0;
        }

        private static long EvalInner(object eqtn, Tuple<int, int> tuple, object eqtnArgs)
        {
            return 0;
        }
    }
}
Thomas Weller
  • 55,411
  • 20
  • 125
  • 222
0

how should I be handling a case like this?

The correct way is your Option 1. That doesn't make compiler "mad", because in fact declaring a variable and initializing it later is allowed construct (not only for this particular case) from the very beginning, and compiler should be able to handle it correctly w/o problem.

IMO, the only drawback (or better say inconvenience) is that the keyword var cannot be used, so the type must be specified explicitly. But some people that are against using var in general would say this is indeed a good thing :-)

Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343