DEV Community

Kelly Brown
Kelly Brown

Posted on • Edited on

What else would I change in a C# rewrite?

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)

Collapse
 
peledzohar profile image
Zohar Peled

Nice series! looking forward for part 3 :-)

Collapse
 
saint4eva profile image
saint4eva

There is a switch expression in C#

Collapse
 
thebuzzsaw profile image
Kelly Brown

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.