Why this compile error

2019-01-15 00:54发布

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 =

5条回答
三岁会撩人
2楼-- · 2019-01-15 01:15

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.

查看更多
在下西门庆
3楼-- · 2019-01-15 01:20

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.

查看更多
女痞
4楼-- · 2019-01-15 01:26

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.

查看更多
狗以群分
5楼-- · 2019-01-15 01:30

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!

查看更多
相关推荐>>
6楼-- · 2019-01-15 01:30
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);
查看更多
登录 后发表回答