Given following classes-
class Point
{
public int X { get; set; }
public int Y { get; set; }
}
class PointWrapper
{
public Point Value { get; }
}
the statement
var p1 = new PointWrapper { // Currently valid
Value = { X = 0, Y = 0 }
};
is already valid. No need to specify type of property Value within the initializer. Even new keyword is not required (using new is an error actually). But Point p = new { X = 1, Y = 2 }; is not valid. We have to write
Point p = new Point { X = 1, Y = 2 }; // Currently valid
My proposal is, when the type of an object is known, make the type-name optional with initializers. In other words, convert the right hand anonymous typed object to the specified type enabling-
Point p = new { X = 1, Y = 2 }; // Proposing
It should not affect the case when the type is not already known or specified like-
var p = new { X = 1, Y = 2 }; // Currently valid, no conversion
Same goes for collection initializers, so that this-
List<Point> list = new List<Point>{
new Point { X = 0, Y = 0 },
new Point { X = 1, Y = 1 }
};
can be written like-
List<Point> list = new {
{ X = 0, Y = 0 },
{ X = 1, Y = 1 }
};
Related- #16648
Akin to #35.
The following two mean very different things:
var p1 = new PointWrapper {
Value = { X = 1, Y = 2 }
};
var p2 = new PointWrapper {
Value = new Point { X = 3, Y = 4 }
};
They mean the following:
PointWrapper p1 = new PointWrapper();
p1.Value.X = 1;
p1.Value.Y = 2;
PointWrapper p2 = new PointWrapper();
Point temp = new Point();
temp.X = 3;
temp.Y = 4;
p2.Value = temp;
The difference becomes very apparent when the property is readonly or is not initialized to a value. For example, if Point were instead a reference type and null, the shorter syntax would fail with a NullReferenceException.
@HaloFour Then this "child" object initializer is a potentially dangerous but valid construct, without generating any warning/error. Don't know why was it introduced in the first place. I know there is a workaround to make it work. But I think this construct should be made safe without requiring that workaround, in 3 steps-
new keyword can be made optional for "children" initializers. Only root initializer having new will suffice for children.@gulshan
Don't know why was it introduced in the first place.
It was introduced specifically to allow initializing readonly properties. That behavior is shipped and cannot be changed.
Instead of just being a syntactic sugar, initializers should be reworked to support initializing read-only (get-only) properties of an object. In this case, the initializer will be required to initialize all read-only properties, unless they have default values. Then initializers will work with upcoming record types also.
Initializers cannot initialize readonly properties, for many reasons. Assuming that the property is actually some 1:1 match to a field, that field is readonly, and it's illegal in the CLR to write to said field outside of the constructor. And for readonly properties that don't share such a relationship with a field it wouldn't make any sense. At best you'd be hoping that there's some 1:1 relationship between the readonly property and some constructor parameter, but that would be absolutely impossible for the C# compiler to automatically determine. The C# team has already shied away from such automatic behavior for positional deconstruction. If you want something that smells like an initializer for constructor parameters, you have named parameters.
As for record types, you would initialize them via the constructor, not their properties.
Making initializers work with read-only properties is another discussion, IMHO. In this thread, I think we can stick to the main proposal, initialize to known type instead of anonymous type when possible.
This was discussed as part of the "target-typed new" proposal (https://github.com/dotnet/csharplang/issues/100).
I'll go ahead and close the Roslyn issue. The discussion should continue on csharplang. Thanks
Most helpful comment
Akin to #35.