Has the C# spec (team? committee?) ever considered this object creation syntax?
Asked Answered
M

6

12

I've never posted a question of this nature before, so if it's not proper for SO, just don't hurt my feelings too bad and I'll delete it.

In the interest of keeping everything I care about as close to the left margin as possible, I keep wishing I could write something like:

DataService1.DataEntities dataEntities = new(constructorArg1, ...)

I think another reason is I like the extra screen real estate I get by using var when the type is already present on the right side of the assignment, but my brain has too many years of looking for the type on the left side. Then again, being stuck in my ways isn't such a good reason to wish for a spec...

Mccluskey answered 25/4, 2011 at 16:45 Comment(8)
Hmm, this is interesting, +1. I, too, like it better than the var option because it gets the type information on the left where it belongs. (Now get off my lawn!)Unintelligent
I note that the question has votes to close based on "subjective and argumentative". The question is neither subjective nor argumentative; the question is a straightforward factual question about whether a particular feature ever came up for discussion in the language design meeting; that's a question about an objective fact. Just because few people know the answer does not make a question subjective or argumentative.Indefinite
@Eric, whether it came up for discussion or not is not a programming question. This will turn into "should it have been discussed" or "should they support it", which is subjective and argumentative.Conclave
@Ken: the StackOverflow FAQ suggests that on-topic questions include questions about the design of tools that are unique to the programming profession, and this is such a question. I don't see any heated argument about what should or should not have been done going on here.Indefinite
@Eric, I think the underlying reason this thread worked out is because the knowledge seekers like me were being enlightened by a definitive authority.. it would be like superman arguing with the hologramsMccluskey
Funny, I was just thinking about the opposite: doing away with the new operator. E.g. var dataEntities = DataService1.DataEntities(constructorArg1, ...)Trici
@ProfK, would that get confused with a static factory method?Mccluskey
@AaronAnodide No, it is a static factory method, with syntactic sugar on top. However, replaces Create and does away with you having to code your own Create.Trici
I
25

Has the C# design committee ever considered this object creation syntax?

Yes, we have. We considered it a couple years ago. As evidence of this claim, see the last paragraph of my article here:

http://blogs.msdn.com/b/ericlippert/archive/2009/01/26/why-no-var-on-fields.aspx

The consensus of the design team was that this was a "nice to have" feature but not sufficiently compelling that it was worth the considerable cost of designing, implementing, testing, documenting and maintaining the feature.

I note also that the comments to the blog entry I linked to are very negative about the feature; it seemed like a lot of people found the syntax unattractive. That was also points against doing the feature.

However, the proposed syntax becomes particularly nice if you can combine it with other language features that promote the concise declaration of immutable types; if we do such a feature in a hypothetical future version of the language, then the syntax you propose becomes more compelling.

I note further that we in general resist features that require inference from "outside" to "inside"; we prefer that type information flow from the inside out. Consider for example this problem:

M(new(blah));

Suppose M has two overloads, one that takes a C, and one that takes a D. Is that "new C(blah)" or "new D(blah)"? It could be either. Now we have to analyze both! And if they both work then we have to figure out which is better.

It gets worse. Suppose you have

M(new(new(blah)));

where again M takes a C and a D, and C has two constructors that take an E or an F, and D has two constructors that take an G and an H. Which of:

M(new C(new E(blah)));
M(new C(new F(blah)));
M(new D(new G(blah)));
M(new D(new H(blah)));

is chosen, and why?

When you reason from outside to inside you quickly get into "combinatoric explosions" where the number of cases to analyze becomes O(cn) in the depth of the nesting.

C# does reason in this manner for lambdas and that is one of the hardest parts of the compiler to make performant and correct, believe me. We're not eager to add a similar feature to constructors. If we were to add this syntax it would probably be limited to scenarios in which the type was unambiguously known by analyzing the left hand side of a variable declaration or assignment expression.

(As always, I note that Eric's musings about hypothetical future language features in unannounced and entirely fictional products that do not have schedules or budgets is for entertainment purposes only, and not to be construed as a promise of any particular future product with any particular feature set.)

Indefinite answered 25/4, 2011 at 16:52 Comment(4)
I'd like to hear more about your ideas on hypothetical features which make working with immutable types easier. The lack of language support in that area is one of my main annoyances with C# currently.Villeinage
@CodeInChaos: The feature is too inchoate at this point to even blog about it; the ideas we've been kicking around mostly revolve around a concise syntax for saying "this is an immutable record" and then having the constructor and accessor properties generated automatically for you -- kinda like anonymous types do. Right now there is a lot of "ceremony" around declaring an immutable type that leads to a lot of boring boilerplate code. It would be nice to eliminate it.Indefinite
But you have int[] f = { 1, 2, 3 }; in C#, I mean that too is outside to inside. I think C# can cause an ambiguity error when type is not mentioned either on left or right, compile if type is mentioned only in either of the sides, and prevent if right and left evaluate to wrong expressions..Keister
@nawfal: In the case you mention the thing on the right hand side of the assignment is not actually an expression. The compiler has special handling code for array initializers; it verifies that every element in the initializer is compatible with the element type of the array type.Indefinite
V
2

It infers from the left side backwards to the expression. And if the expression is non trivial that gets ugly quite fast.

You can understand what an expression does by just looking at the expression itself almost always(except some lamda type inference), and that isn't possible with your syntax. With lamdas the gain is quite large, so the complicated inference is a good trade-off for lamdas, but adding that complexity for such a trivial feature is a bad trade-off for simple variable initialization.

You could special case "assignment to variable where the outmost expression on the right side is a new expression." Sounds very inelegant to me. Especially since the var feature already achieves something very similar, while being more flexible.

Villeinage answered 25/4, 2011 at 16:50 Comment(4)
(random shot here) what if it were "fooType fooIdent = var(arg1, ...)"? would the uniqueness of the var keyword taking a parameter make it work? That is - var(..) would be substituted with new T, and T is whatever the type of the expression is on the left.Mccluskey
That still has the problem of the type inference from the result inward. If you just see var(arg1, ...) you still don't know what it means. This kind of type inference is certainly possible, but I'd avoid it unless the gains are significant.Villeinage
if I read the Dragon book cover to cover would I understand? :)Mccluskey
@Gabriel: Unlikely. The dragon book is a great book for learning theory and practice of compiler design for straightforward imperative-control-flow languages like C or FORTRAN. It goes into a fair amount of depth on the basics of lexing, parsing, symbolic analysis, optimization and code generation. IIRC it does not go into detail about tricky problems of semantic analysis like the one CodeInChaos is talking about.Indefinite
H
2

If you want to create an object of the same type as the member used to store it without having to repeat the type name, you can use "Stockton new". The downside is that you have to repeat the member name in the initialization. Here's how it looks:

class Program
    {
       private static T New<T>(out T item) where T : new()
       {
           item = new T();

           return item;
       }

       static Dictionary<Int32, Int32> _member = New(out _member);

       static void Main(string[] args)
       {
           Dictionary<Int32, Int32> local = New(out local);
       }
}

In addition, we can extend this method to create concrete classes for corresponding interfaces with a couple of simple overloads:

public static IDictionary<TKey, TValue> New<TKey, TValue>(out IDictionary<TKey, TValue> item)
{
     item = new Dictionary<TKey, TValue>();

     return item;
}

public static IList<T> New<T>(out IList<T> item)
{
     item = new List<T>();

     return item;
}

Now you can write this:

IDictionary<Int32, Int32> local = New(out local);

An abomination, a curiosity, or a useful technique? You decide.

Hyperaemia answered 25/4, 2011 at 18:37 Comment(0)
E
1

There is now a championed proposal and an implementation of this feature precisely. We are likely to see it in C# 8.0.

See https://github.com/dotnet/csharplang/blob/master/proposals/target-typed-new.md

Eject answered 13/11, 2018 at 15:47 Comment(0)
E
0

Not sure about that but if you are looking to keep things to the left, you could instead use:

var dataEntities = new DataService1.DataEntities(constructorArg1, ...)
Expectorate answered 25/4, 2011 at 16:47 Comment(2)
The second part of his message talks about this, I think.Thracian
i mentioned that in the last paragraph of my questMccluskey
I
0

Also how about having:

DataType dt;

Mean the same as:

DataType dt = new DataType();

And then:

DataType dt = { ParamOne = 1, ParamTwo = 2 };

Is the same as:

DataType dt = new DataType(){ ParamOne = 1, ParamTwo =2 };
Isla answered 25/4, 2011 at 16:54 Comment(8)
Definitely no. DataType dt; is not, and should not be mistaken for, the same thing as DataType dt = new DataType();. The latter calls a constructor, that's an important distinction that should be preserved in the syntax.Unintelligent
Obviously it's not going to fit the existing language of C#. At all. But my preference at this point is to move towards a language that just doesn't have nulls all over the place. The single most common error I see in code is "null reference" error. Which, sure, comes from sloppy programming, but is also (as I see it) the one flaw in the design of C# as a language. It's also an impedance mismatch with the way certain types work (like int).Isla
Languages that don't have a concept of null have been considered before, but they don't really work. Just like numerical systems without the notion of 0. You're far too limited in expressivity without the ability to express, well, nothing. Now, if you're arguing that C# should move more towards the RAII idiom (as found in C++), then I might be more inclined to agree with you. There are lots of problems with the IDisposable pattern, and I think this is a source of more bugs than null-references (especially because NRE can be easily caught at debug time, whereas memory leaks are tricker).Unintelligent
Ah, never said to abolish null. My point is that int defaults to 0. Why not to null? Why do I have to go out of my way to get a nullable int, but not a nullable reference type? Also, there's a big difference between changing default behavior of variable declaration and abolishing the very concept of null. As for which errors are most common, I can only report what I see. And what I see most often is null reference exceptions.Isla
Don't forget that DataType dt; is not the same as DataType dt = null;.Chevron
@svick, good point. That makes the syntax even more disjointed, since int dt results in an initialized variable where as DataType dt does not.Isla
@mcl, that's not correct. Both int dt; and DataType dt; result in an uninitialized variable. The same syntax can be used for fields, but they are always already initialized when you can access them to their default value.Chevron
@svick, of course you're right. To my muddled mind, the statements look an awful lot alike and give completely different results depending on context. Uninitialized locals don't even compile, whereas fields are implicitly initialized. And then the fields behave as I said, value types get a default, reference types get a null. I would prefer reference types get a default instance rather than null. And while I'm dreaming, I'd like the syntax I proposed. Of course, this would probably lead to bugs more subtle than my hated "null reference" error, which is reason enough to leave it alone.Isla

© 2022 - 2024 — McMap. All rights reserved.