Syntactic Sugar is Good™. What is a while
loop if not sugar for goto
? let me tell you what it is: easier to read and less error prone. Yes, you can do less with while
than with goto
and conditionals... that does not make it not sugar. It is sugar all the way down (I mean abstractions). We are getting to a point where we are closer to writing what we intend the code to do rather than how to do it. And that is Good™.
"Easy to read code" is not the same as "familiar code".
Familiar code is code that you look at a glance and you say "pft, I know what that is, I have seen it a thousand times". Easy to read code is code that anybody who knows the language can understand independently of prior exposure or experience.
For example, this is familiar code:
int total = 0;
for (int index = 0; index < list.Count; index++)
{
total += list[index].Amount;
}
This is easy to read code (Also less error prone and with less wtf per minute):
int total = list.Sum(item => item.Amount);
I think Java does something like this:
int total = list.stream().mapToInt(item -> item.getAmount()).sum();
You will never have an off-by-one error with one of these.
By the way, you may also consider Linq to be sugar:
var total = (from x in list select x.Amount).Sum();
You may also consider it declarative code. It expresses intent, instead of instructions.
By the way, beyond readability. As rule of thumb, the less code you have to write the better. It means less opportunities for error. It also means more productive developers.
Although this is marginal for some syntactic sugar, it is a decisive factor for some. For example, you do not have to write a foreach
to iterate on a list, it is sugar – usually – for using an IEnumerator
. Not always, the compiler can optimize the iteration, it does so for arrays.
You, of course, can do much more with a for
loop or using IEnumerator
instead of foreach
. Similarly, you can do much more with goto
. Yet, foreach
will prevent you from an off by one error or similar, and the compiler will know it does not have to check bounds.
By the way, please do not try to do the equivalent of async/await
yourself. At least not in any production code. Furthermore, have you seen await foreach
? These could seem like a new capabilities, as it uses a lot of work on the part of the compiler. However the truth that you could do them without the help of the compiler... except the code to do them is horrible, very easy to get wrong.
If you understand lambda expression, you get that after =>
(->
in Java) comes code. Then lambda bodied members make sense. You are already learning to write properties, don’t you? You could just learn that this is the way you do it.
By the way, it is worth noting that by using expression bodied members, you are limiting the body to be an expression (and no statements). It is in the name. Thus, it is good that we get throw
and switch
expressions. Arguably a language does not need statements, there are languages like that.
Properties are sugar for getter and setter methods. Why is it ok to use properties? If anything is a new capability of all that I mention here, it is properties. They are the only way in C# to get a getter and setter to appear as a single member to the type system.
In defense of ??=
...
We have this code:
if (random == null)
{
random = new Random();
}
We could make it shorter by using ?:
random = random == null ? new Random() : random;
This sometimes is convenient, however it is not really better readability. Plus, we managed to write the variable three times instead of two.
Let us use ??
to reduce it further:
random = random ?? new Random();
This is clearer. Less error prone, still convenient. However, can we write our variable only once and thus avoid repeating ourselves?
Yes, with ??=
:
random ??= new Random();
Hopefully this gives you some appreciation for this feature.
By the way, with the upcoming target-typed new
expression, you could write:
random ??= new();
Which is easier to write, even if not necessarily easier to read (you may have to look elsewhere to know the type).
Aside, did you know that lambda expressions (not to be confused with expression bodied members) are sugar? They make an anonymous class which exposes a method with the signature needed. All captured variables become fields of the anonymous class. Of course, lambda expressions have very limited capabilities to create classes. However, they save you a lot of work, a lot of horrible code.
Java folks who wrote listeners in Java 5 should remember that it is easier to implement them all in your class instead of making a class for each listener and figure out how to keep encapsulation. Then they got anonymous classes, and afterwards lambda expressions. And the horrible code that it would have been in Java 5 became easy to write, easy to read, less error prone... however, it is still making a class under the hood.
And we say those are functional features. Yeah, we add less capable syntax and call it adding a paradigm. Paradigms are sets of restrictions shown to be good. At least according to Robert "Uncle Bob" Martin, in the book Clean Architecture, paradigms provide constraints. We of course have to make it sugary, so that the right thing to do is the easy thing to do.
Java took a lot of syntax from C and C++. Is knowing C++ an excuse to not learn Java? I do not think so. Similarly knowing Java should not be an excuse to not learn C#. Even if some Java folks continue to claim they are the same language. If you are going to ignore the differences, you are going to ignore the differences. The first rule of Tautology Club is the first rule of Tautology Club.
Although, I am not saying that you should use all the sugar, much less enforce the sugar. You need to reach an agreement on what is considered good style for your project (you know, naming conventions and stuff). And that might include that you are not going to use expression bodied methods, for example. Not because you know Java, but because that is what you agree on.
public string FullName => Firstname + " " + LastName;
Foo Foo { get; set; }
. Let the compiler generate the backing field for you! (Unless what you are trying to do is write a set-only property, which is frankly a very strange thing to do.)??=
by never allowing the field to be null in the first place, and never writing it more than once. If the field is correct upon initialization then you never need to write it and you never need to check it for null. If your intention is to use??=
for lazy initialization, useLazy<T>
instead.