Proposal: Constructor expression and Object literal #1614
Replies: 18 comments
-
Why do we need a completely different syntax to accomplish the following? Func<Project> factory = () => new Microsoft.NET.Sdk.Web.Project {
TargetFramework = Framework.Netstandard20,
RunCommand = "dotnet",
RunArguments = "blazor serve",
LanguageVersion = "7.3",
Packages = {
new Package("Microsoft.AspNetCore.Blazor.Browser") { Version: "0.4.0" },
new Package("Microsoft.AspNetCore.Blazor.Build") { Version = "0.4.0" },
new CliTool("Microsoft.AspNetCore.Blazor.Cli") { Version = "0.4.0" }
}
}; |
Beta Was this translation helpful? Give feedback.
-
@HaloFour what happens when Additionally, in the case of the Finally, the ask here (again, if I understand correctly), is to have an implementation that does not require a delegate, which is more cumbersome to create. For instance, you would be able to use |
Beta Was this translation helpful? Give feedback.
-
@Mike-EEE
Object initializers don't care what the type is. You can assign any valid expression to any writable property.
I don't see why this is a good thing. You're creating instances of classes and that uses the I also want to say that I've seen proposals seeking to allow omitting
Having to name the type is not cumbersome. You'll be required to do that if you plan on assigning it to a field or passing it as an argument to a function anyway. And frankly I don't see why you'd tie some alternative construction syntax with factory generation. Those concerns are entirely orthogonal. And both are solved just fine by the language today. It's generally considered a bad idea to have two ways of expressing the exact same thing, especially when the only real benefit is very slight savings in the number of overall characters. |
Beta Was this translation helpful? Give feedback.
-
@HaloFour With object literals, I am actually proposing a json-like format, for object creation. Object literals are language independent but .net platform dependent. Parsing object literals will be supported by BCL or nuget library at runtime, when the compiler is not present. To parse an Another point is, object initializes does not play nice with immutable properties. And the class/type designer has little control over the initialization. But constructors give the designer full control. I have tried to mention these points in the main proposal also. It is a bit lengthy to read though. |
Beta Was this translation helpful? Give feedback.
-
Can't you just use For example, I can do the following using the Roslyn APIs to construct a method call, with a ExpressionStatement(
InvocationExpression(
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
IdentifierName("System"),
IdentifierName("Console")),
IdentifierName("WriteLine")))
.WithArgumentList(
ArgumentList(
SingletonSeparatedList<ArgumentSyntax>(
Argument(
LiteralExpression(
SyntaxKind.StringLiteralExpression,
Literal("Hello World"))))))) Also, your object literals proposal sounds more like blittable (#187) constants, which has come up in a few different discussions, but I don't think it has a proposal of it's own. |
Beta Was this translation helpful? Give feedback.
-
The thing that I don't understand about this proposal is the need for You say that deferring object construction like this can reduce GC pressure. Can you explain how exactly would that be achieved, especially compared with deferring using a lambda and |
Beta Was this translation helpful? Give feedback.
-
You can already do that today using the Microsoft.CodeAnalysis.CSharp.Scripting package. I'm not sure parsing your .nobj files would be significantly more efficient than that, at least assuming you want to make sure every object literal does the same thing as when you treat it as constructor expression. For example, I think .nobj parser would pretty much have to include all of Roslyn's binding logic (the part of the compiler that determines that the |
Beta Was this translation helpful? Give feedback.
-
@svick Constructor expression was the focus of the proposal. Type is a part of that. I think if a feature like constructor expression is implemented, a specially designed type like Regarding object literals, as it is based on language syntax, a subset of the language parser aka Roslyn has to be used to parse them in runtime. If targeted, it can be very minimal in my opinion. Regarding, type resolution of the object literals, I think in the runtime, the type should not be resolved solely based on the literal or the nobj file. Rather parser will take a type argument and will try to validate the object literal against its constructors. The using statements will be useful for tooling purpose while hand-editing the .nobj files. This was not in my mind during the proposal though. After seeing your comment, it came to me. I would like to make one more point. This proposal does not enable anything new, which is not already possible now. This is proposal is about convenience. So, comments like "you can already do this using so and so" are all mostly true. But that's not the point. The point is whether assumed convenience is worth the implementation cost (in some near or distant future). |
Beta Was this translation helpful? Give feedback.
-
If you don't like the name public delegate T Constructor<T>(); As for it being a value type, I don't get how you imagine that would work. Are you expecting the BCL to add yet another completely new expression tree API built on structs?
That is the point. It's not convenient to have two almost identical flavors of syntax to accomplish the same task in the language. Preferring to omit |
Beta Was this translation helpful? Give feedback.
-
What kind of functionalities? So far, the only difference between
It's not clear to me how that would be an improvement. Today, if I write e.g. Lambdas can allocate a lot when they close over local variables, but
I think it's more than a subjective opinion and that a lot of people would find it confusing that the syntax that's promoted as "you can now avoid typing |
Beta Was this translation helpful? Give feedback.
-
I wish, subjective things remain subjective and open for discussion. I also wish there are choices, if it is not obviously/objectively bad. I don't think This feature should never be promoted as "you can avoid typing |
Beta Was this translation helpful? Give feedback.
-
I love the concept of being able to do XAML like things from code -- that is describe an entire object builder pipeline without actually allocating those objects. I'm not sure if the current proposal would work though. XAML does its magic by having readers and writers, and having plumbed the depths of the XAML source code, the logic is quite complex. To replicate a similar function with a concise C# syntax, a lot of the analogous parsing logic would have to be recreated or borrowed. For instance, there's specific syntax handling of handling arrays, lists, dictionaries, let alone all of the known supported types. It isn't a trivial thing to be able to populate a list or dictionary with members via XAML -- support for that was explicitly baked in, and that code is rather brittle when it comes to user defined types. I have built my own custom XAML parsing to handle cases that the stock parser cannot handle, and I'll tell you that it is very brittle and cumbersome. It's one of those, "I have a problem. Ah hah, I know what to do: I'll use XAML to solve it! Crap. Now I have two problems." type situations. While the library I built is extremely robust and powerful, it has also been a continual thorn in my side since it has required regular routine maintenance over the years to add explicit support for new scenarios where some developer tried doing something seemingly trivial that didn't work since it involved some new data type or collection type or some unsupported construction scenario. Speaking of construction scenarios, if something like this ever came to be, we would definitely need it to support factory methods and builders. My current thought is that something like this could be achieved using something like Lambdas expressions, which also support compilation. Am I missing something vital? Here's a working sample using today's C#:
Having the C# compiler compress all that XAML specific cruft down to the sample from the original proposal would require making the C# compiler very away of a lot of XAML specific constructs, which wouldn't be advisable. However, having the C# compiler use Lamdas expressions and have a syntax for simplying the code from |
Beta Was this translation helpful? Give feedback.
-
Would there be a huge benefit if the C# compiler could handle this code which removes the calls to new? I see the economy of the saved keystrokes, but I'm not sure if would be worth the effort.
|
Beta Was this translation helpful? Give feedback.
-
Such logic would also benefit from having another helper like this so that objects could be initialized inline with arbitrary logic as needed. Problem is that this extension method would be visible everywhere... But some special by-ref syntax sugar could eliminate this need for this extension method to actually be declared in any public API by exposing some specific keywords or grammar to achieve the same effect and have the
|
Beta Was this translation helpful? Give feedback.
-
Using the concept above, maybe something like this could work -- which is simply adding the ability to call a method that looks like it is implemented inline using
|
Beta Was this translation helpful? Give feedback.
-
@marksmeltzer perhaps a new keyword altogether rather than |
Beta Was this translation helpful? Give feedback.
-
@Mike-EEE I've just submitted a proposal #2556 to add the initialization syntax described above to support arbitrary code execution in instance initializers. |
Beta Was this translation helpful? Give feedback.
-
Background
new
is optional for constructor calls. And in some other languages like Kotlin (and Rust to a certain extent) thenew
is totally omitted. Omittingnew
makes long nested constructor call "trees" concise, easy to follow and reason about. Some UI frameworks like Flutter is using these nested constructor calls to build up UI or views, instead of separate UI specific resource files. Not usingnew
makes it concise. This is also similar to Elmish-like view creation.new
-less constructor expression is already in use- in attribute declarations.Proposal
This proposal is actually consists of two related proposals-
Constructor expression:
new
- likeMyObject(id: 1)
(while the constructor call beingnew MyObject(id:1)
).new
is associated with instantiation and allocation of objects. As per this proposal, unlike constructor calls, a constructor expression will not allocate a new object into the memory. It will create and return an object of the generic value typeConstructor<T, TArg1, Targ2, ...>
, containing a valid set of constructor call arguments for typeT
. This constructor object can later be used to create/allocate one or more new objects of type T from the saved arguments.Constructor<T>
will have a methodpublic T New()
. This method will create/allocate an object from the expression and then return it.New()
will call theNew()
of the children constructor expressions and instantiate them and call the constructor with them. This means, anew T1(new T2(new T3()))
constructor call can be coded asT1(T2(T3())).New()
with constructor expressions.Object literal:
.nobj
file extension) containing a single object literal, defining how an object of a certain .net type can be constructed based on one of its constructor. For convenience, .net object files can also contain using statements.Some examples
This is what can be a project file (an example blazor project file taken from web) based on .net object literal
project.nobj
-Which would be equivalent to current
project.csproj
-This is an attempt to replace a XAML file-
equivalent to-
It has to be admitted that XAML has some powerful features added over plain XML and which cannot be easily replicated. This is another attempt to use constructor expressions instead of XAML markup-
equivalent to-
Just to note, these are mocks and not working code. And these construction expressions and object literals would be a lot shorter if arguments were not named.
Benefits
Some open questions
ConstructorArguments<T>
orCtorArgs<T>
instead ofConstructor<T>
?implicit
operator inConstructor<T>
returningT
objects? Then justMyClass x = MyClass()
would work instantiating the object without calling theNew()
method-MyClass x = MyClass().New()
. To note in this case,var x = MyClass()
will still be generating aConstructor<MyClass>
object named x inferred fromMyClass()
.Alternatives
new
optional in object creation.new
expression" (VS 16.8, .NET 5) #100 is already being worked on, which offers conciseness of some sort.Previously I proposed a .net object notation format based on object initializers on Roslyn repository. This proposal is superseding that one- dotnet/roslyn#16648
Beta Was this translation helpful? Give feedback.
All reactions