16

Consider the following C# code:

using System;
class Program
{
    static string string1 = "AAA";
    static string string2 = string1 + string3;
    static string string3 = "BBB";

    static void Main()
    {
        Console.WriteLine(string2);
    }
}

I wrote some code like this earlier today and was expecting string2 to contain the value AAABBB, but instead it just contained AAA. I did some reading on the order of initialization of static variables, but it seems preferable to me that some type of warning or error would have been generated during compilation.

Two questions:

  1. Why is such code allowed to compile successfully? (and if the answer is: "because that's how the C# spec is written", then why was it written that way? Are there reasons I'm missing why this doesn't preferably always just throw a compile-time error?)

  2. Is there any way to get a compile-time warning or some other kind of flag if I end up inadvertently writing this kind of code again in the future?

RSW
  • 1,649
  • 1
  • 17
  • 37
  • 4
    _*me thinks* there's no way that actually results in `"AAA"`... copy/paste, run...oh wow it does! Mind blown O_O as I upvote and hope Skeet or Lippert will answer this_ – Tim S. Aug 05 '13 at 20:22
  • If this is a problem, don't initialize at declaration, but instead run a static constructor where you do the assignments. – spender Aug 05 '13 at 20:24
  • 5
    ReSharper gives a warning for this. – cadrell0 Aug 05 '13 at 20:27
  • 1
    @spender It's not a problem if I *know* about it. The problem is if I inadvertently write this kind of code and nothing warns/flags me about it – RSW Aug 05 '13 at 20:27
  • Strongly related: [Is the order of static class initialization in C# deterministic?](http://stackoverflow.com/questions/3681055/) – H H Aug 05 '13 at 21:05
  • 1
    And the relevant quote: _17.4.5: It is possible for static fields with variable initializers to be observed in their default value state. However, this is strongly discouraged as a matter of style._ – H H Aug 05 '13 at 21:07
  • http://stackoverflow.com/a/660611/56778 – Jim Mischel Aug 05 '13 at 21:38
  • @Henk If it is strongly discouraged by the spec itself, it makes me wonder why it is allowed at all. Does anyone know if there are use cases that require being able to "observe static fields with variable initializers in their default value state"? And if not, then why wouldn't something that is "strongly discouraged" by the spec not automatically get flagged with some kind of warning from the compiler? – RSW Aug 06 '13 at 15:20
  • A warning would have been nice but the list of features has to end somewhere... I don't think this particular case is very serious. – H H Aug 06 '13 at 15:56
  • @Henk yah...not serious to you cuz it didn't just bite you in the @ss! =) But more seriously, if the compiler generates a warning for "variable v is declared but never used", then I don't think that this is any less "serious" than that...especially when the C# spec itself "strongly discourages this as a matter of style" – RSW Aug 06 '13 at 16:01

4 Answers4

6

For question 2:

Tools like ReSharper catch these things. There might be a setting inside of Visual Studio that you can turn on for more verbose compilation output.

enter image description here

Here's the code after the Reshaper cleanup that produces "AAABBB"

class Program
    {
        private const string String1 = "AAA";
        private const string String2 = String1 + String3;
        private const string String3 = "BBB";

        static void Main()
        {
            Console.WriteLine(String2);
            Console.ReadLine();

        }

    }

As a side note, I'd say since we usually read from top to bottom it seems logical for the initialization to happen the same way as described in 10.4.5.1 of the C# specification.

Jon Raynor
  • 3,804
  • 6
  • 29
  • 43
  • I would be interested to know if Visual Studio does indeed have a setting to catch/flag this, since we don't have R# where I work. – RSW Aug 06 '13 at 15:23
  • I agree with the "logicalness" of doing initialization from top to bottom. However, if these fields weren't static, the compiler would have thrown an error. *That* seems more logical to me. – RSW Aug 06 '13 at 15:24
  • @RSW - True, but to get that reasoning a person from the C# design team would have to answer. :) – Jon Raynor Aug 06 '13 at 15:57
  • I just ran it through VS2012 Premium with Code Analysis but no warning whatsoever. – H H Aug 06 '13 at 16:04
6

If you think of your static initializers as logically part of the static constructor, things make a lot more sense. In your case, it's just as if you had written:

private static string String1;
private static string String2;
private static string String3;

static Program()
{
    String1 = "AAA";
    String2 = String1 + String3;
    String3 = "BBB";
}

The reason that static initializers don't work the way you want is that it's impossible in the general case for the compiler to reorder things. It could in this case, and in many others. But if you consider second-order effects such as I mentioned in my answer to a similar question, the compiler can't reliably reorder anything.

It would be confusing in the extreme for the compiler to do that reordering "sometimes."

Community
  • 1
  • 1
Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
  • Thanks for the answer. It does provide insight into *how* things are working. What I'm curious about, however, is *why* the compiler allows such a scenario to slip by at all. Why doesn't it at least flag this with a warning? Are there use cases that demand this type of flexibility, such that the compiler happily lets them slip by? Seems to me that since such usage is strongly discouraged by the C# spec, most uses of this type of behavior in the wild are unintentional, as was the case with me. – RSW Aug 06 '13 at 15:34
  • @RSW: Eric Lippert, formerly on the C# development team, has blogged a bit about the pros and cons of warnings. I suspect the compiler team decided that this wasn't enough of a problem to justify a warning. See http://ericlippert.com/2012/07/17/should-c-warn-on-null-dereference/ for a somewhat related case. – Jim Mischel Aug 06 '13 at 16:04
  • Lippert also links to [this very interesting article](http://blogs.msdn.com/b/ericlippert/archive/2011/03/03/danger-will-robinson.aspx) explaining the decision process used to decide what should produce warnings. The scenario that I'm proposing makes it through the first four of his criteria, but possibly fails on the last two, which are: (5) "Can another tool produce the warning?", in which case R# can, and (6) "Will the user discover the error immediately upon testing anyway?", which is debatable. I would be interested to know if the C# team actually considered this one and rejected it. – RSW Aug 06 '13 at 17:01
3

As to #1: The reason that the code is allowed to compile is because, according to 10.4.5.1 of the C# spec, "the static field variable initializers of a class correspond to a sequence of assignments that are executed in the textual order in which they appear in the class declaration." That means that your string2 variable is unambiguously initialized to "AAA" + null. It's arguable that you should at least get a warning... as to why your IDE chose not to warn you, I don't know.

With regards to #2: I don't know. This seems like a question that would be more appropriately if tagged with your IDE as well.

Andrew Coonce
  • 1,557
  • 11
  • 19
1

It's by design.

The static field variable initializers of a class correspond to a sequence of assignments that are executed in the textual order in which they appear in the class declaration.

C# has a definite assignment policy which means that, for example, fields are initialized automatically. Therefore your string3 is automatically initialized to null and so, as far as the compiler is concerned, it already has value.

This means the output of string2 is string1 + null (which is simply string2) and so there is no reason for the compiler to throw any errors (although I do see how a warning for this would be useful).

keyboardP
  • 68,824
  • 13
  • 156
  • 205
  • If `string1`, `string2`, and `string3` weren't static, the compiler wouldn't have let this code compile at all. But since they're static, it lets it get by with no problem at all. If it's by design, then my question is "why the difference"? – RSW Aug 06 '13 at 15:37
  • The reason a compile-time error is thrown in that case is because it's illegal for instance variables to refer to `this` (which you're implicitly doing http://msdn.microsoft.com/en-us/library/aa645759%28v=VS.71%29.aspx). By making them non-static, you're saying `this.string2 = this.string1 + this.string3`. – keyboardP Aug 06 '13 at 16:41
  • Yes, but think about the underlying reason that is the case. And so I still ask "why the difference". If the value of instance variables may be undefined at this point, thus making references to them invalid, why should the compiler allow references to static fields that are used in a similar way, *just* because the compiler happens to initialize static fields to default values earlier than it does with non-static fields? – RSW Aug 06 '13 at 16:48
  • So...my point and my question still remains...are there really any use cases where this is desirable? And if the C# spec itself strongly discourages using it this way, why not prevent examples like this from compiling...or at least flag them with a warning? – RSW Aug 06 '13 at 16:48
  • 1
    But that's the key point with static variables. The compiler won't reorder their initialization times, they'll remain in the textual order and they're guaranteed to use existing values. Therefore the definite assignment policy, whose main function is to ensure uninitialized memory isn't accessed (out of the `unsafe` context), is adhered to. C# is discouraging you from accidentally accessing uninitialized memory which you're not doing with the static keyword. However as I said earlier, I agree that a warning would make sense here and I can't answer for why the design team chose not to flag that – keyboardP Aug 06 '13 at 16:59