Reputation: 62246
Why if I write
void Main()
{
string value = @"C:\";
if (!string.IsNullOrEmpty(value)) {
string sDirectory = Path.GetDirectoryName(value);
}
}
it compiles.
And in case if I write
void Main()
{
string value = @"C:\";
if (!string.IsNullOrEmpty(value))
string sDirectory = Path.GetDirectoryName(value);
}
It doesn't ?
It's clear that from pure functional point of view the declaration of the variable in the second example is useless, but why it magically becomes usefull in first example, so ?
The IL code produced by both examples is exactly the same.
IL_0000: ldstr "C:\"
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: call System.String.IsNullOrEmpty
IL_000C: brtrue.s IL_0015
IL_000E: ldloc.0
IL_000F: call System.IO.Path.GetDirectoryName
EDIT:
Forgot to mantion that to produce the IL
code for the second case (so the case which is not compilable), it's enough to compile without string sDirectory =
Upvotes: 9
Views: 432
Reputation: 7160
I am not sure for C#, but this is the case for other languages: because { } open a block and therefore a new scope. If you omit them, then the variable is being declared in the scope of Main
and so will only be declared some of the time, and so the compiler doesn't know afterwards whether sDirectory
exists or not.
Upvotes: 1
Reputation: 1500515
The production for an if
statement is in section 8.7.1 of the C# spec, and it goes like this:
if-statement:
if ( boolean-expression ) embedded-statement
if ( boolean-expression ) embedded-statement else embedded-statement
The start of section 8 of the C# spec explicitly talks about the embedded-statement production after giving the specification for it:
embedded-statement: block empty-statement expression-statement selection-statement iteration-statement jump-statement try-statement checked-statement unchecked-statement lock-statement using-statement yield-statement
The embedded-statement nonterminal is used for statements that appear within other statements. The use of embedded-statement rather than statement excludes the use of declaration statements and labeled statements in these contexts. The example
void F(bool b) { if (b) int i = 44; }
results in a compile-time error because an if statement requires an embedded-statement rather than a statement for its if branch. If this code were permitted, then the variable i would be declared, but it could never be used. Note, however, that by placing i’s declaration in a block, the example is valid.
Note that an assignment counts as an expression-statement - but a local variable declaration doesn't. (That's a declaration-statement, as in section 8.5.)
In terms of a design decision, it makes no sense to declare a variable that you can't then use - so it's good that the compiler stops you from doing it.
Upvotes: 20
Reputation: 18843
string value = @"C:\";
if (!string.IsNullOrEmpty(value))
string sDirectory = Path.GetDirectoryName(value);
The second statement is what is considered an embedded statement.. if you want to use sDirectory within what is called a "Code Block" wrap { } around it. inline statements like what you are trying to do in my opinion make for poor readability..
string value = @"C:\";
string sDirectory = string.Empty; should be even better way to code..
if (!string.IsNullOrEmpty(value))
{
sDirectory = Path.GetDirectoryName(value);
}
or this
both cases now should compile
string value = @"C:\";
string sDirectory = string.Empty;
if (!string.IsNullOrEmpty(value))
sDirectory = Path.GetDirectoryName(value);
Upvotes: 1
Reputation: 160892
The first version with the brackets declares a new local scope within which you declare a string variable, the second version doesn't - the variable declaration and assignment is interpreted as a single embedded statement which may not include variable declarations, hence the compilation error.
Upvotes: 5
Reputation: 72870
Your second form tries to use what is effectively two statements (a variable declaration and a variable assignment) where only a single statement could be used. Think of it as:
if (!string.IsNullOrEmpty(value))
string sDirectory;
sDirectory = Path.GetDirectoryName(value);
You can see this won't compile!
Upvotes: 6