12

Why is the code below

private static List<WorkflowVariableDataSet> MergeDatasetsListBranch(out List<WorkflowVariableDataSet> datasetsList)
{
    if(datasetsList == null)
        datasetsList=new List<WorkflowVariableDataSet>();
    datasetsList=new List<WorkflowVariableDataSet>();
    return datasetsList;
}

generating an error at the first if statement:

Out parameter 'datasetsList' might not be initialized before accessing.

I know it should be uninitialized at this point, but the word might suggest that the error lies in possible uninitialized object accessing (when it's not even accessed, it's the reference, that is checked). Ofc that doesn't happen with ref keyword, but I'm curious how is the reference checking violating out-parameters policy.

EDIT I've edited the question and the example: the out object will be initialized inside the method anyway. The question is: WHY uninitialized object cannot be null compared? How is that different from:

object o;
if(o==null)
    ...
Tarec
  • 3,268
  • 4
  • 30
  • 47

3 Answers3

20

Compiler Error CS0269

Use of unassigned out parameter 'parameter' The compiler could not verify that the out parameter was assigned a value before it was used; its value may be undefined when assigned. Be sure to assign a value to out parameters in the called method before accessing the value. If you need to use the value of the variable passed in, use a ref parameter instead.

So treat an out-parameter as unassigned. You are the one who is responsible.

So just remove the if:

datasetsList = new List<WorkflowVariableDataSet>();

If you want to process a list that is passed to this method use ref intead (as suggested above):

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • I didn't ask how to solve the problem. That's obvious. I'm asking why can't I compare uninitialized object reference to `null`. – Tarec Feb 05 '14 at 12:49
  • @Tarec: I thought the compiler error with the explanation was clear. You cannot access a variable that is unassigned. The compiler is very strict in this case since an `out` parameter must be initialized by the method not the caller. You cannot even return from the method without assigning a value to an `out`-parameter explicitly. – Tim Schmelter Feb 05 '14 at 12:51
  • I'm not accessing the variable anywhere - I'm just checking whether its reference is null or not and I've never encountered the situation, that I'd be unable to do that on uninitialized object. – Tarec Feb 05 '14 at 12:53
  • @Tarec: `datasetsList == null` is a comparison. The [== Operator](http://msdn.microsoft.com/en-us/library/53k8ybth.aspx) evaluates the expression on both sides before the comparison can take place. So it will read the value of `datasetsList` which triggers the compiler error. Consider that it would be a property instead, then set a breakpoint in the getter. You'll see that it will be hit on this comparison. – Tim Schmelter Feb 05 '14 at 12:55
  • 1
    "I'm not accessing the variable anywhere - I'm just checking whether its" Which? Either you're not accessing the variable, or you're checking it; these two statements cannot possibly both be true. – Jon Hanna Feb 05 '14 at 12:58
  • 1
    I'm not accessing the uninitialized object, but I'm checking the reference. How is it possible for a REFERENCE to not have ANY value? If it's not pointing anywhere, it should be NULL. If I'll do: `object o;` `if(object==null)..` then it's not initialized and yet, compiler does not protest as expected. – Tarec Feb 05 '14 at 13:01
  • 3
    @Tarec: you are comparing apples and oranges. The compiler error is `out`-parameter specific since an `out` parameter is not the same as other variables. It **must** be initialized in the method not outside. The error is just a help for you to remember to initialize it before you access/read it. – Tim Schmelter Feb 05 '14 at 13:04
  • Thank you. Then the message is wrong. It should state, that 'Out parameter 'datasetsList' is definately not initialized' and that it could appear ONLY in the line of initialisation. – Tarec Feb 05 '14 at 13:07
  • 1
    @Tarec: no, the `out`-parameter can be initialized at this point if the caller passes the list. So you can even see it's value in the debugger but you cannot acess it otherwise. But the compiler doesn't need to care about since the rule is strict: don't touch it before you haven't assigned anything in this method. – Tim Schmelter Feb 05 '14 at 13:19
  • 2
    Note that a compiler always tries to take the shortest way since one of it's most important requirement is to be efficient. Consider how much more expensive (in terms of CPU cycles) it would be to check if someone passes `null` or an initialized object from anywhere **at compile time**. This is impossible to check and it's redundant anyway. – Tim Schmelter Feb 05 '14 at 13:28
1

Because whether you've buggy code that never initialises the parameter, or buggy code that sometimes doesn't initialise it, it's still the same bug.

There's no point having a separate error message for the same bug depending on whether it hits in all or just one code paths; if there is a single code-path where the parameter is used before initialising, then it's has that error, and if there isn't a single code-path, then it doesn't.

So if we consider:

private static List<WorkflowVariableDataSet> MergeDatasetsListBranch(out List<WorkflowVariableDataSet> datasetsList)
{
    if(_someBooleanField)
       datasetsList = null;
    if(datasetsList == null)
        datasetsList=new List<WorkflowVariableDataSet>();
    return datasetsList;
}

Here the use of an uninitialised out parameter might or might not happen, but that suffices to mean it has the same error.

As far as the error goes, there really isn't any significant difference between these two cases.

And therefore the error message uses might, even in cases where it will always apply.

Jon Hanna
  • 110,372
  • 10
  • 146
  • 251
  • I've edited the example: It'll always be initialized within the method and the error remains the same. – Tarec Feb 05 '14 at 12:50
  • No it won't, you're still testing `if(datasetsList == null)` before it's initialised. That's both an error and also redundant (it can't be null, because it hasn't been set to anything - including null - so why test?) – Jon Hanna Feb 05 '14 at 12:57
0

Out arguments need not be initialized prior to being passed, the calling method is required to assign a value before the method returns.

Modified your code

 private static List<WorkflowVariableDataSet> MergeDatasetsListBranch(out List<WorkflowVariableDataSet> datasetsList)
            {
                return datasetsList = new List<WorkflowVariableDataSet>();
            }
Prashant Mehta
  • 484
  • 2
  • 11
  • 30