6

I create objects by initializing the fields in this way:

class Example
{
    public int a;
    public int b;
}

var obj = new Example
{
    a = stream.ReadInt(),
    b = stream.ReadInt()
};

Is it always for field "a" to be initialized before field "b"? Otherwise, an unpleasant error can occur when the values from the stream are subtracted in a different order. Thanks!

UPD: In the comments, many did not understand what I mean. I will clarify the question. Do these records always be identical in behavior on different (.NET, Mono, etc) compilers?

First:

var obj = new Example
{
    a = stream.ReadInt(),
    b = stream.ReadInt()
};

Second:

var a = stream.ReadInt();
var b = stream.ReadInt();

var obj = new Example
{
    a = a,
    b = b
};
  • 2
    In the same thread, behavior of execution is *always* sequential. Also that "initialization" is really just assignment hidden with syntax; it's (generally) equivalent to `var obj = new Example(); obj.a = stream.ReadInt(); obj.b = stream.ReadInt();`. Expecting that to work reliably is the same as expecting `Console.WriteLine("First:" + stream.ReadInt()); Console.WriteLine("Second:" + stream.ReadInt())` to work reliably in a well-defined manner ~ it does! :) – user2864740 Dec 24 '17 at 08:16
  • The Language Spec doesn't seem to say anything about the order in which the object initializers is executed, so it might depend on implementation. – Sweeper Dec 24 '17 at 08:21
  • 1
    @PeterDuniho It doesn't say anything in section 7.6.10.2. Can you give the section number? – Sweeper Dec 24 '17 at 08:24
  • 1
    Perhaps this is relevant: [link](https://stackoverflow.com/questions/495616)/order-of-operations-using-object-initializer-syntax – Thejaka Maldeniya Dec 24 '17 at 08:47
  • 1
    Possible duplicate of https://stackoverflow.com/questions/495616/order-of-operations-using-object-initializer-syntax – Peter Duniho Dec 24 '17 at 08:49
  • 1
    Can you create a constructor and use it? If so, the order will always be preserved... – Thejaka Maldeniya Dec 24 '17 at 08:50

2 Answers2

5

IMHO, the relevant part of the C# language specification is reasonably clear. It could be less vague, but I don't see a way to interpret it that would allow for out-of-order initialization:

An object initializer consists of a sequence of member initializers

[Emphasis mine]. The word "sequence" necessarily implies an order. Maybe that doesn't seem normative, but their example does:

An instance of Point can be created and initialized as follows:

Point a = new Point { X = 0, Y = 1 };

which has the same effect as

Point __a = new Point();
__a.X = 0;
__a.Y = 1;
Point a = __a;

If a compiler author were to reorder the assignments, the program that they output would not comply with the above example.

More generally, it's informative to look at the rest of the language in that section, as the language designers went to great pains to make sure the feature worked intuitively, including for nested assignments with object initializers to only assign a fully-formed object to the parent property.

All that said, it really shouldn't matter. If you have a class where the order of assignments to two or more properties affects the final outcome, you've got a pretty hazardous class on your hands. Such design ought to be avoided at all costs, as it is way too easy to break in any case, even when the compiler rules for object initialization are reasonably clear.

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
  • 1
    Yes, it can be dangerous. I use this approach when i'm need to subtract data from a stream. I can first subtract everything into local variables, but this affects usability. Here is an example from a real project: `var command = new CastDirectedSkill{ objectId = reader.ReadInt(), skillSlot = reader.ReadByte(), targetPoint = reader.ReadVector2() };` Adding local variables is not always convenient, so this question was born. – Vyacheslav Spirin Dec 24 '17 at 09:02
  • Evaluation and assignment are separate issues. For example: Evaluate X, assign to A, evaluate Y, assign to B Evaluate X, evaluate Y, assign to A, assign to B Evaluate Y, evaluate X, assign to A, assign to B in https://stackoverflow.com/questions/495616/order-of-operations-using-object-initializer-syntax – Thejaka Maldeniya Dec 24 '17 at 09:06
  • @ThejakaMaldeniya: not really. The assignment is intrinsically part of the `member_initializer` element of the grammar. It would not make any sense to lift the right-hand-side of those initializers, execute them first, and then execute the assignments, never mind then _reorder_ the expressions, nor would doing so comply with the examples in the spec. – Peter Duniho Dec 24 '17 at 09:08
0

This construct is known as an "Object Initializer" and should not be confused with object/field initialization. It is pretty syntax to modify an object after it has been fully constructed.

Per an example in 7.6.10.2 - Object Initializers of the C# 5.0 Specification:

An instance of Point can be created and initialized as follows:

Point a = new Point { X = 0, Y = 1 };

which has the same effect as

Point __a = new Point();
  __a.X = 0;
  __a.Y = 1; 
Point a = __a;

The specification example implicitly covers the ordering of the assignments (which implies evaluation of the RHS in a specific order) as well as the total order of the assignment of the created object (ie. the object will not be assigned to a field prior to the object initializers being processed).

user2864740
  • 60,010
  • 15
  • 145
  • 220
  • 1
    I was reading that part as well, but think this can be clearer. The object initializer in the example can have the "same effect" as setting `Y` first then `X`, since you are just assigning them constant integers, but I guess nothing is perfect... – Sweeper Dec 24 '17 at 08:39
  • The behavior illustrated in the example is definitely .NET's implementation and having different behavior would be quite egregious, especially as it breaks the expected eager evaluation in C#. – user2864740 Dec 24 '17 at 08:42
  • @Sweeper Along with "An object initializer consists of a sequence of member initializers .. a member initializer that specifies an expression after the equals sign is processed in the same way as an assignment (§7.17.1) to the field or property.", my minds at peace. – user2864740 Dec 24 '17 at 08:49