I have a class that can be created with data or with a binary reader (which it uses to deserialize it's data). It does additional initialization with the data, and since I don't want to duplicate that code, I want to chain the constructors. Right now this chaining looks like this:
public Item(string id, int count = 1) { /*...*/ }
public Item(BinaryReader reader) : this(reader.ReadString(), reader.ReadInt32()) { /*...*/ }
Is order in which these read calls are made deterministic?
Clarification: I was talking about the order in which read.ReadString()
and reader.ReadInt32()
are called.
The exact behavior of the order of evaluation when calling methods with multiple arguments is documented in the C# specification Run-time evaluation of argument lists which states:
During the run-time processing of a function member invocation (Compile-time checking of dynamic overload resolution), the expressions or variable references of an argument list are evaluated in order, from left to right, as follows:
So in your case the order will be:
reader.ReadString()
reader.ReadInt32()
Now, there are a couple of problem with this, mainly related to readability and exceptions.
The fact that you ask which call order is the correct one, the next programmer might have the same question. It would be better to refactor this into a different solution that doesn't come with a lot of questions attached.
Additionally, if you pass in a null
reader
you'll get a NullReferenceException
instead of a ArgumentNullException
, even if you should happen to validate this parameter not being null
inside the constructor, simply because all this evaluation will happen before the constructor body executes. There are hacks to "fix" this but they are worse than the code you already have.
To refactor into a "better" solution (my opinion) you would create a factory method like this:
public static Item Create(BinaryReader reader)
{
if (reader == null) throw new ArgumentNullException(nameof(reader));
// optional: handle exceptions from reader.ReadString and ReadInt32
var s = reader.ReadString();
var i = reader.ReadInt32();
return new Item(s, i);
}