2021-10-04

What is wrong with C#

This is part of a series of posts in which I am documenting what is wrong with certain popular programming languages that I am (more or less) familiar with.  The aim of these posts is to support a future post in which I will be describing what the ideal programming language would look like for me.  

I will be amending and revising these texts over time.

What is wrong with C#:

  • The garbage collector.
  • Curly braces.
  • Member initializers execute in a static context. 
    • This is far worse than Java's deficiency of member initializers not having access to constructor parameters; This renders the language almost unusable.
  • Delegates are superfluous and problematic.
    • They could have been implemented as single-method interfaces, as in Java, thus keeping the language simpler.
    • The ability to have an anonymous method implementing a delegate is far less useful than the ability to have an anonymous class implementing an interface.
  • The built-in collection model is lame:
    • Arrays implement the `IList` interface, which might initially seem like a great idea, until you realize that `IList` has `Add()`, `Insert()` and `Remove()` methods, which of course cannot be honored by an array.
    • The `IReadOnlyList` interface was added as an afterthought, and it is not a super-interface of `IList`. 
    • The `IReadOnlyList` interface does not have an `IndexOf()` method. This poses problems that cannot be solved by adding an extension method, because the object implementing `IReadOnlyList` may have its own `IEqualityComparator`, which the extension method will be blissfully unaware of.
    • Enumerators still have to implement the legacy, non-generic `GetEnumerator()` method.
    • Fluent style (Linq) is limited to working almost exclusively with `IEnumerable<>`.
  • Built-in events are problematic. They are unsuitable as a language feature and should have been left for runtime libraries to implement.
    • If one event handler throws, the rest will not be invoked.
    • If one event handler causes another event handler to be removed, the removed event handler will still be invoked.
    • An event is a special kind of thing which cannot be passed to a function, not even by reference. (As all properties are.) As a result, you have to always hand-code the addition of a handler to an event, and to also separately hand-code the corresponding removal. It is absolutely impossible to pass an event to a function, along with a handler and a boolean flag specifying whether to add or remove the handler. This makes it impossible to gather all symmetric initialization and cleanup operations in one place, which in turn leads to buggy software.
  • Extension methods
    • They are a hack.
    • They are one of the most calamitously misused features of the language.
    • Java has shown how to do this right with default interface methods. 
    • Allegedly, default interface methods will also come to C#, but it is not like extension methods will ever be removed from the language to undo the harm they have caused.
  • Parameters declared with `ref` or `out` 
    • Prior to the introduction of tuples, `ref` and `out` parameters could sometimes come in handy, but only in very rare cases. 
    • At the same time, they are the single most calamitously misused feature of the language.
    • Now that tuples are part of the language syntax, parameters by reference are nothing but a liability.
  • Names of variables poison not only nested scopes, but also enclosing scopes.
    • For example, if you declare a local variable inside the curly braces of a `for` loop, you are not allowed to declare a variable with the same name after the closing brace of the `for` loop. Duh?
  • The language runtime is shared with other languages like Visual Basic, and some decisions have been made there in favor of Visual Basic rather than C#.
    • For example, when you dereference a null pointer you do not get a "Null Pointer Exception", you get an "Object Reference Is Not Set To An Instance Of An Object" exception.  (Presumably because the words "Null" and "Pointer" would cause epileptic seizures to Visual Basic programmers.)
  • No compiler-enforced function purity.
  • No compiler-enforced immutability.
  • Certain things about the runtime are completely bonkers, for example:
    • The notion of a "current directory", which is a piece of global state shared across all threads, and even across all AppDomains within a process.  (So much for AppDomain isolation!)
    • The behavior of the `ThreadAbortException`.
    • Collecting a stack trace is a ridiculously slow operation.
    • Throwing an exception is a stupendously slow operation.
  • No namespace-private visibility
    • C# has always provided assembly-private visibility, which is much more useful than Java's package-private visibility, however at some point Java fixed that by introducing modules, and the ability to specify which packages are exposed by a module. Now C# lags behind Java in that it does not support namespace-private visibility, which means that everything is visible to everything within an assembly, which can easily lead to chaos if the assembly is large. There is a way to somewhat mitigate this by using partial classes as if they were namespaces, but it is hacky.
Feedback is more than welcome: you'd be doing me a favor. However, be aware that blogger sometimes eats comments, so be sure to save your text before submitting it. If blogger eats your comment, please e-mail it to me.

No comments:

Post a Comment