The C# compiler is a bit ... old fashioned ... and won't do static analysis. So it breaks on seemingly correct code like this:
MyStruct s;
bool inited = false;
foreach( Something foo in ACollection )
{
if( foo.Test() )
continue;
if( inited )
s.DoSomething();
else
{
s = foo.GetMeAnS();
inited = true;
}
}
Note: the unusual problem is that "s" is a struct. If it were a class, I'd simply init it to null. This struct has no meaningful "uninited" state, and I don't want to pay the performance cost of initing something I immediately throw away, just to satisfy a weak compiler.
The code (should be) fully correct: it's impossible to access s until s has been inited. (I've copy/pasted from actual code, but edited-out long method names for simplicity).
C# compiler in Mono used to allow this, but now it doesn't. Nothing has changed except the compiler, which now gives an error on unassigned variable.
Is there a code way to tell it to shut up and mind its own business? :) I don't want to fiddle with changing compiler-settings (if possible) because the code is compiled by other people/orgs - I'd prefer a code way of fixing the problem.
Is there a code way to tell it to shut up and mind its own business?
The compiler's business is implementing the C# specification. The code you've written should not compile according to the C# specification. The s.DoSomething()
call is reachable without s
being definitely assigned, therefore your code is broken. That's not the compiler's fault. If the Mono compiler used to allow it, that was a bug which has apparently now been fixed.
The simplest way of fixing it is to definitely assign the value, of course:
MyStruct s = new MyStruct(); // Value will never actually be used
There are plenty of cases where we (as humans) can tell that something will never happen, but the compiler can't. Here's another example:
public int Foo(int input)
{
if (input >= 0)
{
return input;
}
else if (input < 0)
{
return -input;
}
// This is still reachable...
}
We know that every int
input will go into one of those if
bodies, but the compiler will still (correctly) give a compilation error on the above code, because the closing brace is reachable and it's a non-void method.
Your claim that "The code (should be) fully correct" is according to your reasoning, not the C# specificiation... and the compiler is only meant to care about the latter.
One thing to note: the specification doesn't even care about the fact that we do actually set inited
to true in some cases. Even if it always had the value of false
, it's still just a local variable, not a constant expression. Here's a simple example demonstrating that with no loop:
static void Main()
{
int x;
bool condition = false;
if (condition)
{
Console.WriteLine(x);
}
}
This still gives an error: "error CS0165: Use of unassigned local variable 'x'"
From section 8.7.1 of the C# 5 specification:
The first embedded statement of an
if
statement is reachable if the if statement is reachable and the boolean expression does not have the constant valuefalse
.
Here the expression is condition
, which is a local variable. A local variable is not a constant expression in technical terms, even if it will never change. If you make it a local constant instead, it will compile:
static void Main()
{
int x;
const bool condition = false;
if (condition)
{
Console.WriteLine(x);
}
}
Now there's a warning about the body of the if
statement being unreachable - but there's no error.
See more on this question at Stackoverflow