Menu

No, C# is not ‘getting bloated’

August 21, 2022 - Programming

Recently, it’s been almost mematic how common the discussions about how C# is getting bloated have been; people have suggested basically creating a new language to ‘get rid of the bloat’, often citing things like Kotlin. That could be it’s own topic but in my experience the people who hype Kotlin haven’t used Java since Java 7. Furthermore, it seems that most of the “Hype” surrounding it seems to just be based on pointing out issues with Java, while ignoring that Kotlin, like any language, introduces it’s own issues. But, again, this is not something I have enough experience with to speak at-length about.

Back to C# being “bloated”. The typical argument seems to be that because there are so many ways of doing the same thing, and because there is one “proper” way, the language is bloated with “old ways”.

Some examples often cited as evidence of this bloat are things like pattern matching, switch expressions, and default interface implementation.

C#’s pattern matching in particular gets criticized, because it is now the “proper” way to check for null:

    if(value is null){}

Often when this is raised, it is of course compared to comparisons to null; the claim is, oh, those old ways of null checking are “old code” and not the proper way anymore. But this argument actually sort of illustrates a flaw in it’s own reasoning- those “old ways” were never the “proper” way of checking for null even going back to C# 1.0, nor are they equivalent to the pattern matching approach. A second “pattern matching” implementation that is shown as the “proper way” to check that an item is not null in a similar fashion is to use the pattern matching like this:

    if(value is {}){}

This is sometimes hoisted as an example of “OMG language so bloated” But fundamentally this is just reusing an expressive language feature and turning it’s versatility into some kind of disadvantage, by abusing it to perform a common task in this way. Consider that you can do something similar by abusing some other new operators that were added in previous language versions:

if((String.IsEmpty(value?.ToString()??""))

The ability to misuse expressive features in this manner to create “lots of ways of doing the same thing” is hoisted as a detriment to the language but what it means is the language is getting more expressive.

Basically, “is” in the original case avoids issues from overloaded equality or inequality operators. This is why it’s generally pushed as preferred. Thing is, we’ve had Object.ReferenceEquals which could be used to test for null in the same scenario – avoiding equality/inequality operators – which was indeed pushed as a “proper” way to check for null since C# 1.0. The fact that many of the writeups about “C# bloat” aren’t aware of this seems to suggest that the “proper” way doesn’t actually mean anything anyway. “newbies” aren’t going to learn “bad ways” by using equality/inequality. This is all notwithstanding the rather cogent argument that if you have an operator overload that messes up null checking, that is the bug, not using the “wrong” null check somewhere that causes it; though, perhaps that’s a matter of opinion.

Switch expressions lead to frustration that there are “too many” ways of doing branching code, or something of that sort. I mean, we’ve got if and switch and now switch expressions? Woah, too many. But now- hold the phone son. What about loops? You’ve got While, Do…While, Do…Loop, For, ForEach… All of which are just loops. Do…Loop is the “proper” way to ensure that a loop goes through at least once, but most people just hack it and still use a While loop. Again, using different constructs for the same task doesn’t mean you aren’t doing “proper” programming. You don’t *have* to use Do…Loop if you want to ensure a loop runs through at least once. There’s nothing wrong with finding other approaches that ensure that.

Default Interface Implementation is often considered to be redundant; That they really don’t serve much of a different purpose from members in an abstract base class. Like Do…Loop versus While, they are similar constructs but they operate slightly differently, which can be a benefit when designing a system architecture. Default interface implementations are stateless and have no inheritance association- eg if you have a second interface deriving from the first and it doesn’t operate polymorphically; that is, if you have a class that inherits an interface with a default implementation which does not itself implement it, then, similar to explicit interface inheritance, variables of that class type will be unable to call the member; instead, it can only be done when working with the interface type itself. Fundamentally, the feature is effectively providing the benefit of “traits” as seen in many other languages. Use it, or don’t use it. It doesn’t matter. But at least we should try to understand it before proclaiming it to be “bloat”.

My guttural reaction to many new features is often a somewhat similar “bah humbug”, and from that perspective I can almost see where the argument for “C# bloat” comes from. But I think it is actually emotionally driven, not logically so.

I’d argue that in some sense what is actually happening is we’ve got some more grizzled C# devs who have been working with the language for a while and, for the first time in a long time, these new features appear and they are lost. How can this be? They are “experts!” They’ve been doing this for years!  Instead of realizing that they need to decide between the constant work of keeping up with the programming language they use or at least be comfortable in knowing they are not experts at the language in it’s current iteration, they decide, in a manner similar to the Principal Skinner meme, “Am I out of touch with C#? No, it is the language that is wrong” Don’t know how to use a newer feature? Oh, that’s not because you are lazy or haven’t put the effort in, no! That’s because that feature is *bloat* and therefore you don’t have to learn it.

I actually remember following a similar process with the introduction of linq….  this is language bloat, nobody asked for this, it does what we can already do, I don’t need to learn this, etc. But after digging into it and understanding the feature, it of course became second nature and I feel far more expressive than I did before that feature existed.

What I see when I see people complaining about new language features, and calling it "bloat" and saying that a new language needs to be designed to "remove the bloat" because there are "too many ways to do the same thing" I see a person who is basically saying "Why do we need a hammer? I can smash nails down just fine with the butt of my screwdriver that we got in the previous version, we don’t need two tools that do the same thing" Or possibly even explaining their issue with hammers- "look! It doesn’t even work any better!" because they are trying to smash the nail in with the handle of the hammer the same way they were using the screwdriver.  

Have something to say about this post? Comment!