Why I Started to Feel Differently About C#

hamid
7 min readMay 8, 2015

--

In the mind of every developer I know who loves programming languages, there is always a soft spot or preference for a certain programming language. To me, Python has always been my favorite. I even prefer to use it in job interviews whenever possible. If this isn’t an option, however, I often just choose C++ as it seems pretty much universally acceptable. Occasionally though, I run into interviewers who specifically ask me to code in C#, which I gladly honor, especially it has been one of the primary programming language I use at work for several years now. It wasn’t until very recently that the peculiarity of this situation started to come to my attention: why is it that I refrain from using C# in casual white-board coding or during interviews although I use it on a daily basis? Wouldn’t C# be more widely acceptable than Python and relatively easier to use — especially for an interview — than C++?

A tale of clever choices

Admittedly, it is no coincidence that the author of a book like Cracking The Coding Interview chose to write problem solutions in Java. There are good reasons why languages like Java and C# are more suited to programming interviews. For instance:

  • Automatic memory management
    Whether you prefer automatic or manual memory management, it is definitely one less detail to have to worry about if you’re coding on a white-board or solving a programming question.
  • Libraries
    Stacks, queues, sorted lists, dictionaries, algorithms for searching, sorting, you name it. And although these artifacts are commonly present in almost every programming language out there today, it is relatively easier, syntactically at least, to use a dictionary in C# than it is in C++ for instance. Below are 2 code snippets contrasting the basic uses of .NET Dictionary with C++ STL map.
Using .NET’s System.Collections.Generic.Dictionary in C#
Using STL map in C++ and C++11
  • Platform agnosticism
    You will rarely ever have to worry about using system specific APIs, choosing the right headers or importing the correct packages. It goes without saying that C# does not run on as many platforms as does Java today. However, it is rather the nature of managed languages I am referring to here, i.e. unified, standard and cross-platform libraries that rely on runtimes to tackle all the platform-specific details.
  • Popularity/Market penetration
    Java and C# have huge audiences, are used prolifically in the software industry, have a lot in common, not to mention they both share the syntactic appeal of good old C.
  • Richness of programming constructs
    Whether it’s OOP constructs, e.g. inheritance, virtual functions, abstract classes, interfaces; playing with types, e.g. generics, anonymous types; or concurrent programming primitives, e.g. threading, locks, synchronized execution, you will find more or less everything you need.

So, back to my original thought: why has it never occurred to me that I should be writing C# in casual contexts? I know for a fact that one of the primary, yet subconscious, reasons behind this choice was that (1) I didn’t have enough confidence in my ability to write C# without the rich and extensive facilities of Visual Studio. Another good reason is that (2) C# has gone such a long way since it was introduced more than 10 years ago. It has grown far beyond where it initially started, i.e. yet another managed, statically-typed and object-oriented language that looked a lot like Java, and steadily evolved into a modern, full-fledged and multi-paradigm programming language that has its uniqueness. This evolution, however, has materialized over a fairly long period of time that it probably requires us to take some time to think where we are now and realize how far we have come.

A tale of good compromises

I have always admired certain features of C#, e.g. LINQ. LINQ seemed like an old dream come true with the lazy evaluation and strong functional taste it brought about. It was especially exciting to see such features being presented in a mainstream language that has quite an audience. Ideas like lambda expressions are probably not new at all to anyone who has ever played around with different programing languages. But seeing such ideas available to the hands of an extended audience of developers working on a wide range of applications is pleasurable indeed.

But beyond this, the thing that caught my attention the most was a number of design decisions I came to observe in the evolution of C#, decisions that were made, I think, in quite a careful and thoughtful manner. In my mind, I think these decisions reflect ambition to take the language to next levels while remaining mindful of lessons of the past.

In the following few lines, I present a number of features that showcase some of these design decisions in my opinion.

Careful syntactic choices

  1. Mandating the specification of ref and out parameters in both function definitions and call sites. Contrast this with C++’s ambiguous pass-by-reference that is syntactically identical to pass-by-value. It is not uncommon to see C++ coding conventions trying to dodge this issue by specifying that out parameters must be passed via pointers for instance.
  2. Mandating the specification of virtual/override for virtual/overriding functions. This spares you the need to make guesses or navigate long inheritance hierarchies just to figure out if a method is actually overriding another method in a parent class or not. Issuing a warning in case a method in a subclass is hiding a method in a base class, unless you suppress it by marking the subclass method with the keyword new, goes along the same lines.

Usability-centric features

  1. I am afraid I have to highlight out and ref variables once again here. Realizing that some variables are solely intended to be populated/created by other modules and providing facilities for such occasions, and designing feature syntax as discussed earlier, is surely a sign of careful and usability-centric design.
  2. Auto-implemented properties is another good example of a practical Don’t-Repeat-Yourself kind of feature. Especially that it’s getting even more and more useful in C# 6.0 with initializers and getter-only properties.
  3. Null coalescing operator,
// 
// i.e. being able to write:
//
string name = nameParam ?? "default name";
//
// instead of the more verbose
//
if (nameParam != null)
name = nameParam;
else
name = "default name";
//
// it gets even better: it's chainable!
//
string name = nameParam ?? alternateName ?? lastResortName;

The (early) introduction of Generics

It did not take the authors of C# a long time before they introduced Generics — in C# 2.0 — and started encouraging the developer community to move over to using the generic alternatives of the classic ArrayList and the like. Other signs of careful and meticulous design decisions here are:

  1. Avoiding the duality of int vs. Integer, char vs. Character … etc. that is present in Java. C# Generics simply work directly with primitive types without any underlying boxing/unboxing even though this entailed such code would be binary incompatible with C# 1.x. Given how important, convenient and widely used Generics are now, I tend to believe this was a wise compromise.
  2. Generic type constraints is another good example of carefully designed features. If you have ever had a chance to look at proposals of a somewhat similar feature, i.e. C++ Concepts, that was being considered for inclusion in C++11 — C++0x at the time — but got removed later, will feel that generic type constraints in C# is the useful bare minimum of the notion that is also uncomplicated.

IEnumerable<T> and yield return

Although Java contains an Iterable<T> that can be considered more or less equivalent to C#’s IEnumerable<T>, it is far less emphasized and useful. One of the key reasons, in my opinion, is the absence of an equivalent to yield return. Having such facility that could simply turn any method into a directly consumable generator — without having to implement any special interfaces as is the case in Java — seems like another good design decision that yielded a very practical and useful artifact without sacrificing simplicity.

It is not only that; the absence of this “resumable execution” concept may force a programmer to rewrite her algorithm or use an entirely different one instead of simply being able to have her code yield whatever value it generates at any point along the execution path. For example, think of a recursive program that is generating the power set of some items or maybe a combination of things. For a much simpler example, you might just want to check out a comparison of a Fibonacci sequence generation function implemented in Java vs. C# right here.

Functional Programming

Although a good deal of the functional features currently present in C# was introduced as part of LINQ, which I have already mentioned, it remains pretty interesting to see such a heavy dose of functional constructs, e.g. closures, lambdas, expression trees, introduced to the language in a fairly straightforward and easy-to-use syntax. This is a contribution to the language at a paradigm level that does not only enrich it but also presents carefully-thought out incarnations of beautiful Functional Programming concepts.

Even the introduction of basic things like Delegates, Predicates, Actions and Funcs — instead of adopting a puristic OO approach and attempting to achieve the same effect via design patterns — seems to grant developers the best of what both the OO and the Functional worlds have to offer.

All of these observations had me thinking about C# in a different way. It’s grown into a solid and homogeneous blend of paradigms, features and syntactic sugar inspired from a wide variety of old and modern programming languages. With the new language features planned for the upcoming VS 2015 release, I believe I will continue to follow C# closely but with a completely different perspective this time. And I have little doubt that it is going to be my first suggestion to my next interviewer who does not happen to know Python.

--

--