-2

Will it always works correctly?

public class Test
{
    public static List<int> a = new List<int>{1,2,3};

    public static List<int> b = new List<int>(a);
}

Because when I switched them

public class Test
{
    public static List<int> b = new List<int>(a);

    public static List<int> a = new List<int>{1,2,3};
}

then I received

Exception in user code:

System.ArgumentNullException: Value cannot be null. Parameter name: collection at System.Collections.Generic.List1..ctor(IEnumerable1 collection) at .Test..cctor()

Are there any other things I have to worry about?

Joelty
  • 958
  • 11
  • 34
  • 3
    Same way you read a book.. top bottom - that's how code is compiled. – Adrian Feb 21 '19 at 08:28
  • 1
    the order initialization is the same order you write your own code, on the 2nd sample `a` is simply not defined / is null when you try to pass it as parameter to `b` – styx Feb 21 '19 at 08:28
  • Cause you just had to debug to notice that a is null, so as the error says, that value can't be null. In my opinion this isn't even a question, you just had to think instead of copypasting things.. – Marco Salerno Feb 21 '19 at 08:33
  • @MarcoSalerno but I'm not asking why it is null or why there's an exception, but I'm asking whether there are any other things to worry about while trying to initialize two properties with data of eachother other, than their order. – Joelty Feb 21 '19 at 08:34
  • 1
    @Adriani6 While that's true for field initializers, it certainly isn't true in general. Very few things in C# depend on the textual order of class members in the code, and it's not like C# is a single-pass compiler either (unlike e.g. Pascal, where you *can't* call a procedure that's defined below the call site). – Luaan Feb 21 '19 at 08:45
  • @styx It would be fine if `a` weren't defined - at least that would produce a compiler error. It's actually null, which makes these dependencies in field initializers rather dangerous. – Luaan Feb 21 '19 at 08:46
  • @Luaan Correct, I might of worded my comment wrong as what I said was relating to the code sample in the question. – Adrian Feb 21 '19 at 09:53

2 Answers2

1

If you need deterministic initialization, use a static constructor:

public static List<int> a;
public static List<int> b;

private static Test()
{
  a = new List<int>{1,2,3};
  b = new List<int>(a);
}

The C#/CLR specification ensures the order in which field initializers are executed, but as you've noticed, if you mess up the ordering of your fields in the class, things break down at runtime. That's a bad idea regardless of the contractual behavior, given how easy it is to switch the order of fields in a class.

So my advice is: if the field initializers don't depend on other fields, keep them. If they do, put the initialization in a constructor, where the ordering is very explicit.

The relevant quote from specification:

If a class contains any static fields with initializers, those initializers are executed in textual order immediately prior to executing the static constructor (§17.4.5).

So you can rely on the textual order contractually, but I'd still recommend against it. It's way too easy to break, and people usually don't consider the order of members in the code file of a class important. It would be fine if you got a compilation error, but you don't - if you're lucky, you get a runtime error. If you're not, you're going to be scratching your head with "impossible" situations that had nothing to do with your changes (right?).

Luaan
  • 57,516
  • 7
  • 84
  • 100
  • 1
    That is in no way more deterministic than OPs code. – HimBromBeere Feb 21 '19 at 08:29
  • 1
    no need of an extra static constructor ... fields will get initialized in the order they are defined always – Rahul Feb 21 '19 at 08:30
  • That's untrue. It is the same deterministic as do it in class – Piotr Stapp Feb 21 '19 at 08:30
  • @Rahul There's no need for many things, and yet we do them. Just because something "works" doesn't mean it's a good idea to do it that way. You could define all your fields and locals as `object` or `dynamic`, and yet you probably don't. Why? The same reason why dependencies between field initializers are a bad idea - you're preventing the compiler from helping you realize your mistakes. – Luaan Feb 21 '19 at 08:49
  • Sorry, help me understand your comment. Your posted code is no different than what OP already have. It will never make any difference. Irrespective or static or instance member; when instance member fields will start getting initialize .. they will initialize in the order defined. – Rahul Feb 21 '19 at 08:57
  • @Rahul It is very different, both in the code produced, and in the way the textual code is maintained. I already mention this in the answer itself - order of members is unimportant almost every time in C#, so relying on the order of members in text is dangerous. People just don't treat members the same way they treat lines of procedural code, and there's very little reason to go against the grain here. Heck, even refactoring tools will reorder members according to your whimsy (e.g. alphabetical order, access modifiers, grouping...), breaking your code. And for what? Are the two lines worth it? – Luaan Feb 21 '19 at 09:02
0

Yes as you have observed correctly the order has to remain as below since the member fields will get initialized in accordance order

public static List<int> a = new List<int>{1,2,3};
public static List<int> b = new List<int>(a);
Rahul
  • 71,392
  • 13
  • 57
  • 105