C# The order of fields initialization
Asked Answered
A

2

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
};
Abutment answered 24/12, 2017 at 8:14 Comment(6)
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! :)Leucocyte
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.Omalley
@PeterDuniho It doesn't say anything in section 7.6.10.2. Can you give the section number?Omalley
Perhaps this is relevant: link/order-of-operations-using-object-initializer-syntaxDingbat
Possible duplicate of #496116Notable
Can you create a constructor and use it? If so, the order will always be preserved...Dingbat
N
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.

Notable answered 24/12, 2017 at 8:48 Comment(3)
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.Abutment
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 #496116Dingbat
@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.Notable
L
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).

Leucocyte answered 24/12, 2017 at 8:34 Comment(3)
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...Omalley
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#.Leucocyte
@Omalley 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.Leucocyte

© 2022 - 2024 — McMap. All rights reserved.