Just reiterating: I love C#. I will use it forever. These are just thoughts about how I would approach a spinoff language.
Immutable Arrays
Increasingly, I wish T[]
was immutable. It makes perfect sense that strings are immutable. As a result, there is cognitive dissonance in arrays being mutable. I wish I could concatenate two arrays the way I concatenate two strings.
.NET Core 2.1 introduced string.Create
, which gives you temporary mutable access to the string's storage (Span<char>
).
public static string Create<TState>(
int length,
TState state,
SpanAction<char, TState> action)
A similar solution would work for arrays to avoid unnecessary copies. There could also be an overload that handles one element at a time.
public static T[] Create<T, TState>(
int length,
TState state,
Func<TState, int, T> valueGenerator)
Eliminate Multicast Delegate
I can't remember the original reason for this decision. I believe it was done to support events in GUI frameworks.
It is easy to compose a custom multicast delegates from monocast delegates, but the reverse is simply not possible. We all pay for the cost of multicast delegates even though up to 100% of delegates in a project are monocast. That's an unfortunate fee for using delegates at all.
Fix switch
The switch
statement was lifted straight from C/C++.
switch (input)
{
case 1:
DoTheThing();
break;
case 2:
case 3:
DoTheOtherThing();
DoAnotherThing();
break;
default:
DoDefaultThing();
break;
}
Let's just adopt the if-statement scope rules.
switch (input)
{
case (1)
DoTheThing();
case (2, 3)
{
DoTheOtherThing();
DoAnotherThing();
}
default
DoDefaultThing();
}
Sealed by Default
I don't think classes should be open to extension by default. Inheritance is a very particular superpower in the OOP landscape. I feel that a given class should have to invite inheritance rather than restrict inheritance. In other words, switch to opt-in rather than opt-out.
A class could invite inheritance by marking itself with either abstract
(which requires inheritance anyway) or some other keyword like base
.
public base class Widget
Drop Multidimensional Arrays
Treating an array as multidimensional data belongs to abstractions. Force developers to explicitly decide whether the grid is row-major, column-major, etc.
Drop Equals
The object virtual method Equals
is a relic of the pre-generic era. If you want to know if two references are the same, call ReferenceEquals
. Otherwise, it is up to the type itself to decide whether it has a "value" that can be equated to another. In such cases, I'd much rather work with IEquatable<T>
anyway.
Drop GetHashCode
Following the section above, if two values are not meaningfully equatable, they're probably not meaningfully hashable either. Can two objects be hashable without being equatable? Maybe there should be an interface IKey<T>
that extends IEquatable<T>
and adds GetHashCode
. Otherwise, an IHashable
interface would do the trick. Maybe this could also serve as an opportunity to support hashes of different lengths: IHashable32
, IHashable64
, or others.
Top comments (3)
Nice series! looking forward for part 3 :-)
There is a switch expression in C#
I know, but it's not the same. My proposal here is about fixing the original switch statement to properly allow reasonable blocks of code.