7

When declaring a member

class MyClass {

    AnyClass<WithLong<Generic,Declaration>> myProp =
        new AnyClass<WithLong<Generic,Declaration>>();

}

is quite redundant.

We may find the following, more concise, less noisy:

AnyClass<WithLong<Generic,Declaration>> value = new default();
// or
AnyClass<WithLong<Generic,Declaration>> value = new();
// or
AnyClass<WithLong<Generic,Declaration>> value = new var();

Is there any benefit to the actual redundancy, or any risk on a more concise declaration that I haven't identified ?

Note:
var is actually a good way to reduce noise for local variables:

var dictionary = new AnyClass<WithLong<Generic,Declaration>>();

My question is about extending that concision to member declarations, where the var syntax is not allowed.

11
  • 1
    Just say var dictionary = new new Dictionary<T, int>(); Oct 28 '19 at 14:25
  • 1
    It's not actually redundant; Dictionary<T, int> dictionary is a declaration, while new Dictionary<T, int>(); is a constructor call. var dictionary uses type inference to infer the declared type. In short, this situation is already handled. Oct 28 '19 at 14:26
  • @RobertHarvey: As much as I disagree with OP's suggestion when you get down to brass tacks (see my answer), it's a bit arbitrary to consider type inference only valid one way and not the other way. At face value, for OP's particular example, the compiler could infer either type if the other one was explicitly mentioned. var can't be used unless you immediately instantiate the variable (for obvious reasons), so that would guarantee that you always have both types (declaration+instantiation) available and thus either of them could be inferred by the other.
    – Flater
    Oct 28 '19 at 14:46
  • 5
    This is called "Target-typed new expressions" and has been proposed for C#. You can follow the whole discussion with pro and cons here: github.com/dotnet/csharplang/issues/100
    – JacquesB
    Oct 28 '19 at 15:19
  • 1
    I took the freedom, edited your question a but, trying to describe the actual problem more explicit (so it hopefully becomes more clear what you are after). Please check if I got your intentions right.
    – Doc Brown
    Oct 28 '19 at 20:47
8

Is there any benefit to the actual redundancy, or any risk on a more concise declaration that I haven't identified ?

The C# language team have discussed removing the need for that redundancy by supporting one of the example syntaxes you cite:

class Foo
{
    private AnyClass<WithLong<Generic,Declaration>> value = new();
    ...
}

It's currently in prototype and pencilled in for C# 9.

The C# language team are a very conservative bunch. They do not make changes to the language on a whim and go through rigorous analysis of the pros and cons of a change. The fact that it's made it this far as an idea strongly suggests that there is no objective benefit to that redundancy.

However, there are subjective benefits. Just as some folk dislike using var:

var value = new AnyClass<WithLong<Generic,Declaration>>();

so there will be folk that dislike target-typed new expressions too. And many folk really dislike the team adding new ways of doing the same thing. The team will weigh up those dislikes against the benefits and may yet decide not to release this feature. Time will tell.

2

What changes you are suggesting for member variables is only a change in the compiler. There needs to be no change in the executing CLR. So such a change would be easy to bring. You would need to submit the request with Microsoft. I have also found this redundancy a slightly annoying (only slightly). However the redundancy being contained in one line pacifies me.

One of the reason for developers hate redundancy is bug fixes. When a bug is fixed around redundant code, then we might modify one place and forget the other place. This is a big problem. In this case redundancy is contained in one line, so the chance of modifying only a part of it is remote. And in most of these cases it will throw a compilation error when partly modified. So it is okay in some sense.

1

The core idea

I don't see an issue with using a syntax similar to var variables when declaring class variables:

public class Foo
{
    var Bar = new Bar();
}

All the necessary information is there, it's not ambiguous, and it follows a known syntax (variable type inference).

However, a combined declaration and initialisation is a rare occurence for method members, as opposed to how often it occurs for variables. I suspect this is why it hasn't really been addressed by the language architects.

Your proposed syntax

As you identified, to reduce the redundancy, we should cut down on one of two redundant references. Currently, only this type of syntax is allowed (for variables):

var dictionary = new Dictionary<T, int>();

and not either of these:

Dictionary<T, int> dictionary = new();
Dictionary<T, int> dictionary = new var();

At face value, and what this question is trying to assert, you'd think that these are equally valid.

But are they?

Your example is slightly limited in that you're only considering concrete classes. Consider how this would work for interfaces (or abstract classes). First, the current existing way:

var userService = service.Get<IUserService>();

This works. The compiler identifies the return type of Get<IUserService>() and then sets var to the same type (presumably IUserService in this example).

But for your suggested approach:

IUserService userService = new();
IUserService userService = new var();

This does not work. Without either a concrete object instantiation or a method with a defined return type, the compiler is incapable of determining exactly what you to have happen here.

You may think it's slightly cheaty that I introduce the service method in the existing example (since you can't instantiate an interface), but the point of the matter is that your "type-first-var-last" approach simply doesn't add any reduction to this case, as it would end up as:

IUserService userService = service.Get<IUserService>();

= no shortening.

This leads us to the simple conclusion that the existing approach allows for shortened yet consistent syntax in all cases, whereas your suggestions would be inconsistent or inapplicable depending on whether the used type is concrete or not.

In other words, your suggestion is simply not as good as the existing approach because it can't cover all relevant cases.

The default approach

In the above, I have omitted your first suggestion:

Dictionary<T, int> dictionary = new default();

I would avoid this in general, as default has a defined behavior. For reference types, default(T) specifically returns null.

When I read this suggestion, my initial takeaway was that you're assigning null to the variable. While your syntax is subtly different (no type parameter used), I am opposed to having these very different behaviors both implemented on the same default keyword.

Technically, it could be made the way you want it to. But it's going to have a negative impact on readability, so I don't like it.

4
  • I think you missed the part where the OP wrote "When declaring a member". var dictionary = new Dictionary<T, int>() is not allowed in class member declarations, only in local variable declarations (Robert Harvey seemed to have missed it, too).
    – Doc Brown
    Oct 28 '19 at 16:07
  • @DocBrown: I think my feedback on OP's proposed syntax stills stands, but you are right I missed that point (it wasn't quite clear to me in an earlier version of the question). The existing var syntax seems like a better fit if you were to apply this to class members.
    – Flater
    Oct 29 '19 at 8:34
  • That some new nice syntactic sugar does not cover other related uses which have some slight but very significant differences is not surprising, nor all that relevant, unless fine-tuning the proposal would allow catering to them too. Oct 29 '19 at 15:21
  • @Deduplicator: My point is more that the existing syntax (var-then-type) would also work to cover the suggested new syntactic sugar and therefore it's a better fit.
    – Flater
    Oct 29 '19 at 15:26
0

To truly answer this question, you need to understand how compilers and linkers work. C# essentially does those two things in one step. The difference in the context of where the variables are defined make all the difference in whether what you are asking is feasible.

At the global level, you have to define the size and shape of your class. The fields, methods, etc. have to be explicit and well defined. That information is usually cached so that when the compiler is working on the method implementations, it can reference that class definition structure.

One of the key problems here is that you must know the left hand type at the class level. This is one of the primary reasons that var even works at all. So we can't use var as a return type or a class field declaration.

So if we look at the right hand side, you need a reasonable way to identify the type that needs to be created. Since the left hand type can be an interface (and in my code it often is an interface), we can't just rely on a shorthand new().

The solution that Java 8 provided was to simplify the generic specification, but that's possible because Java's generics are a weak contract unlike Microsoft's generics. The declaration looks like this:

public class MyObject {
    private Map<String,List<MyObject>> cache = new HashMap<>();
}

That might be "good enough" in your scenario. To translate that code into C# it would look like this:

public class MyObject
{
    private IDictionary<string,IList<MyObject>> cache = new Dictionary<>();
}

It reduces the annoying redundancy in the generic declaration, but provides the expressiveness to choose an implementation other than what was defined in the type definition for the field.

It appears that there is a proposal in for a new version of C# to address this, so it will be interesting to see if/how Microsoft addresses this concern. Thank you to JacquesB for the reference.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.