views:

333

answers:

11

Why is this C# code not compiling?

public static Dictionary<short, MemoryBuffer> GetBulkCustom(int bufferId,
    int startSecond,out int chunksize, out int bardatetime)
{
    //const string _functionName = "GetNextBulkWatchData";

    UserSeriesCard currentCard = GetUserSeriesCard(bufferId);

    Dictionary<short, MemoryBuffer> result = null;

    while (currentCard.CurrentSecond <= startSecond)
        result = GetBulk(bufferId, out chunksize, out bardatetime);

    if (result == null)
    {
        result = currentCard.UserBuffer;
        chunksize = currentCard.ChunkSize;
        bardatetime = currentCard.CurrentBarDateTime;
    }
    return result;
}

Errors:

The out parameter 'bardatetime' must be assigned to before control leaves the current method
The out parameter 'chunksize' must be assigned to before control leaves the current method

I can't think of a case where bardatetime and chunksize will end up unassigned..

Edit. I fixed this error by adjusting the code to a logically equivalent one. Honestly I wanted to avoid multiple assigments.

public static Dictionary<short, MemoryBuffer> GetBulkCustom(int bufferId, int startSecond,out int chunksize, out int bardatetime )
    {
        const string _functionName = "GetNextBulkWatchData";

        UserSeriesCard currentCard = GetUserSeriesCard(bufferId);

        Dictionary<short, MemoryBuffer> result = null;
        chunksize = currentCard.ChunkSize;
        bardatetime = currentCard.CurrentBarDateTime;

        while (currentCard.CurrentSecond <= startSecond)
            result = GetBulk(bufferId, out chunksize, out bardatetime);

        if (result == null)
            result = currentCard.UserBuffer;

        return result;
    }
+5  A: 

Your out params are not set up in all codepaths. If you skip the sections of code that are contingent on and while (currentCard.CurrentSecond <= startSecond) and if (result = null) then you must make some sensible default assignment to all of them.

You may know for a fact that the while loop will execute at least once, but the compiler does not know this. In this case you may be able to replace that loop with a do {//logic} while (//condition); alternative.

If you cannot do that, then this construct should enable the compiler to detect deterministic setting of the out variables.

if (currentCard.CurrentSecond <= startSecond)
{
  while (currentCard.CurrentSecond <= startSecond)
  {
    result = GetBulk(bufferId, out chunksize, out bardatetime);
  }
}
else
{
  result = null;
}
Steve Townsend
I know for a fact that if the while loop didn't excute at all, result will end up being 'null' and the baramters should be assigned in the next if bulk
Mustafa A. Jabbar
@Mustafa - see code in edit
Steve Townsend
+3  A: 
if currentCard.CurrentSecond > startSecond 

and

if result is null

they out params will not be assigned.

You could do something like this:

public static Dictionary<short, MemoryBuffer> GetBulkCustom(int bufferId, int startSecond,out int chunksize, out int bardatetime )
{
    //const string _functionName = "GetNextBulkWatchData";


    UserSeriesCard currentCard = GetUserSeriesCard(bufferId);

    Dictionary<short, MemoryBuffer> result = null;

    // initialize with a -1
    bardatetime = -1;
    chunksize = -1;   

    while (currentCard.CurrentSecond <= startSecond)
        result = GetBulk(bufferId, out chunksize, out bardatetime);

    if (result == null)
    {
        result = currentCard.UserBuffer;
        chunksize = currentCard.ChunkSize;
        bardatetime = currentCard.CurrentBarDateTime;
    }

    return result;
}
gmcalab
Of course the caller should know what to do with values such as -1 (i.e. that possibility should be tested explicitly, in case the code "always" succeeds in assigning real values to the variables.) I've had code running well for months until something made it use such default values which I had forgotten to test for. Lots of fun. :)
MetalMikester
@MetalMikester, of course they should check for initialized values in the processing, realizing the -1 would be a invalid value. Either way he is going to have to make sure the values are assigned to the out params before exiting. This is just an example of one approach.
gmcalab
+1  A: 

You may not conceive that those out params would not be set, but as far as the compiler knows it is possible (by virtue of you having a pre-check loop) for control to exit without those params being set.

Jamiec
+18  A: 

If the while loop and "if statement" bodies are never entered then the out parameters are not assigned.

It might be the case that logically you know that those code paths will always be entered. The compiler does not know that. The compiler believes that every "if" and "while" that has a not-constant condition can be entered or skipped.

The compiler could in this case do a more sophisticated flow analysis. The analysis is "before the 'if', result is either null or not null; if it is null then the 'if' body assigns the out parameters. If it is not null then the only way that could have happened is if the 'while' body assigned the out parameters, therefore the out parameters are assigned."

That level of analysis is certainly possible, but the existing flow analysis algorithm described in the spec has some nice properties, namely that it is fast, easy to understand, easy to implement, usually accurate and only gives false positives, not false negatives.

Eric Lippert
thanks Eric. You got exactly what I meant. Simple and to the point
Mustafa A. Jabbar
I just figured out that I am speaking to the compiler himself :D
Mustafa A. Jabbar
+2  A: 

Both of the places where chunksize and bardatetime are assigned are inside some sort of control statement (while or if), which the compiler cannot know if those sections will be entered or note.

As such, there's no guaranteed assignment, as far as the compiler knows, for those two out parameters.

Pete
+1  A: 

If result is null, bardatetime will never be assigned. A parameter declared as out must be set before the method returns. Just initialize it to a default value at the beginning of the method, it should work fine.

Thomas Levesque
A: 

If currentCard.CardSecond <= startSecond the while loop won't run, result will be null and the values will never get set. How does the compiler know what .CardSecond and startSecond will be?

mcintyre321
A: 

You can try to replace the while(){} loop with a do{}while()-loop. In this case the asignment would be guaranteed.

codymanix
A: 

Because for the compiler within a method, just like a local variable, an output parameter is initially considered unassigned and must be definitely assigned before its value is used now check out your implementation of GetBulk.

xdevel2000
A: 

Initialize them as you enter the function.

pascal
A: 

Just wanted to point out that using ReSharper, the plugin itself would highlight what's wrong with this code.

Rafael Belliard