Monday, 8 May 2017

Why I like C#


I was going to talk a bit more about version control and Git after my posts on Agile version control late last year but I have been very busy with a new job.

“it was 20
years ago today”
However, I have had a draft of this post for many years and significantly it was 20 years ago today (actually last Thursday) that I invented C#. I remember it distinctly as a colleague suggested we go out and celebrate "Star Wars Day" after work. I had never heard of Star Wars Day (May the Fourth) before then.

It was during a jovial conversation over a few drinks that I mentioned my idea that Microsoft should invent their own language which, as it turned out, was rather like C#. The reaction was that I was a complete idiot, after all Microsoft were not known for that sort of thing - the closest they had come was COM (Component Object Model) which we had all used and agreed was an unwieldy mess. Microsoft had also invested heavily in their own dialect of Basic called VB (Visual Basic), as well as their C compiler, so why would they want a new language? This was in the days when it was novel for a company to create a language, begun, I guess with Sun and Java.

My idea was oddly triggered by my problems with the latest Visual C++ 4.0 (which we had been using for a few months). I had written a new SCSI library in C++ for some 32-bit code but was greatly dismayed to find that VC++ was not designed to create 16-bit code (for MSDOS, Windows 3.1) or even Win32s - I wanted to use the C++ code with some legacy C applications.

We had also encountered lots of frustrations with C++.  We had done a lot of stuff in C previously but needed some modern languages features. Other languages around like Delphi and Java were not suitable either.

I decided that what was really needed was a language that Microsoft would properly support (C++ support was not good at the time), had modern features that C was lacking (like templates, exceptions and even a few niceties from Java, like GC). There was also a technological development (see next), where Microsoft was at the forefront, which was crucial for my new language.

P-Code

I had been greatly intrigued by p-code since 1978 when I had first used Pascal at University. Pascal (at least the compiler I used) compiled to p-code which thence required a p-code interpreter for whatever CPU you were using. (This idea was later used Java in byte-code and .Net MSIL.) Not long after that I did a lot of programming in Forth (sort of a p-code assembler) on my Commodore 64.

The problem with p-code is that it is slow. Running p-code (via interpreter) is about an order of magnitude slower than the same program compiled to native instructions. A lot of people thought that this would become less of a problem as computers got faster. The trouble with this argument is that while the interpreted code got faster so did the native code. (The idea of p-code also seemed to be fading until Sun created Java, but a lot of people did not like the poor performance of Java.)

However, some people at Microsoft realized that once processors were fast enough you could compile on the fly (later called JIT-compilation) rather than interpret the p-code. Sooner or later the compilation time would become negligible.
"once processors
were fast enough
you could compile
on the fly"

Microsoft had done a lot of research on JIT-compilation, on their (then) flagship language VB. (Basic is an interpreted like p-code).  During the mid 1990s I did some tests and found that VB6 was similar in speed to C++ in many areas due to use of JIT-compilation. All the C++ programmers I told this to just did not believe me (or did not want to).

Why Microsoft needed a new language in 1997

  1. C (and an emerging C++) were the driving force behind almost all the successful MSDOS and Windows products and most C/C++ programmers, myself included (perhaps unfairly), avoid VB like the plague.
  2. C/C++ development was painful and slow. COM was supposed to address this but made it worse!
  3. There were a lot of error-prone areas in C/C++ that could be fixed by new syntax and semantics.
  4. There were a lot of things that could be taken from C++ like exceptions, templates and STL containers.
  5. They could wrap/hide the horrendous Windows API (like MFC did for C++).
  6. There was a lot of interest in Java (especially its innovative memory management system) but Microsoft were impeded using Java for their own ends by a litigious Sun. (Sun had already successfully sued Microsoft for not properly implementing Java.)
  7. A language like Java generating intermediate code (p-code) would be slow but this problem could be alleviated by Microsoft's research into JIT-compilation from the early-mid 1990's.

.Net and C#

I did not really think much more about it but a few years later I started hearing about NGWS, VB7 and later C#. In 2001 I downloaded and tried the C# public beta. It was then that I realized that C# was very similar to the language that I had previously been advocating. My only disappointment was that C# lacked templates and STL-like containers, though C# 2.0 later added generics.

To be fair to Microsoft (and Anders) there were a hell of a lot more great things in C# than I could ever have come up with, though many came from Java and C++. And even since then, C# has added some brilliant new things of its own, like lambdas and LINQ.

C# Problems

You may have noticed from previous posts that I like C# in many ways. I guess this may be partly due to the fact that I feel I independently invented it. However, there are a few things that it got wrong, but remember that these are very small in number compared to the large number of things it got right.  (Also note that some of these things are due to the .Net CLR upon which C# depends.)
  1. One thing that I really thought C# should have had from the start was (what came to be known as) generics. In 1997 I had become a huge fan of templates, and especially the STL, in C++. I remember reading that they would be added later (and were added in C# 2.0). Why not delay the release of C# 1.0 until they were ready? This has caused a lot of problems of maintaining backward compatibility, especially when implementing generic interfaces.
  2. I really hate the containers in C# compared to C++ STL containers or even those of other languages. Related to the previous problem is the later addition of generic containers to replace the earlier ones.
  3. Apart from the containers most of the C# run-time library is excellent. But there were some simple, obvious mistakes which I picked up straight away. An obvious one was that Int.Parse() throws an exception if it encounters a non-digit rather than the more sensible behavior of something like C's strtoi().  This was later addressed with Int.TryParse().
    Another one I encountered almost straight away is that String.Substring() throws an exception if the string is not long enough. This might be good behavior sometimes but more commonly you would just want a shorter string returned rather than throw an exception.
  1. One of the stupidest things in C# is the Decimal type. (Anders seems to have an irrational penchant for stupid numeric types such as the Delphi real48.) As soon as I saw it I thought it would be better and much simpler just to add fixed point facilities to existing integer types, attached as metadata (ie, using an attribute attached to an integer variable).

    I wrote at length about this over a decade ago - eg see Why do we need Decimal?
  2. Const is one of the simplest and most useful additions to C++. I do not know why C# (and other languages) continue to ignore it. TBD: elaborate?
  3. The fact that all static and heap variables are cleared (made zero) at run-time is sometimes unnecessary and inefficient.  What's the point of a huge array being initialized to zeros then immediately have all its values set to some other value(s)?  (Note that the security argument is a furphy.)
  4. I previously mentioned that the behavior of the default test for equality (Object.Equals()) is flawed. (See the C# section in my post on Zero).  Actually having recently used Go (the language from Google) I now realize that all the "object-oriented" stuff, that C# copies almost exactly from Java, is unnecessary and actually encourages poor designs (but at least there is no multiple-inheritance!).  I may talk about this more in a future post on Go.

Conclusion

C# made things so much easier after using C (and then C++) for decades. There are so many things I could mention but a few immediately spring to mind:
  • metadata system which avoids all sorts of configuration problems that plague C due to header files, linking issues, DLL hell, etc
  • the garbage collected heap which frees you from the tedium of having to track who allocated what and who needs to free it and making sure there are no memory leaks and double-frees, etc
But there were many other little things - for example, see the Code Project article that I wrote in 2004 called C# Overflow Checking.

Sure C# borrowed a lot of things from Java, but that is de rigeur for language design (and Java got a lot from C++).

Thursday, 17 November 2016

Overview


Here are links to all of my posts so far, loosely categorized.

Note that there are a few bonus links to my Code Project articles - marked with [CP]


Software Design

Design Principles

Handling Software Design Complexity - what software design all boils down to
DIRE - an obvious thing we often forget
Developer Quality Attributes - or why fixing bugs is not important
Verifiability - software is useless unless you can verify its correctness
Why Good Programs Go Bad - risk avoidance causes software to "rust"
Book Review: 97 Things Every Architect Should Know

Design Practices

Fundamentals of Software Design - 8 ways to create a good design
Agile Design - how emergent design almost always works better than BDUF
Inversion of Control - IOC is a technique for better decoupling using DIRE
Dependency Injection - an example of IOC

Anti-Patterns

Gas Factory Anti-Pattern - a mistake even (or especially) good designers make
Reusability Futility - "Simplicity before Generality, Use before Reuse"
Shotgun Initialization - an example of the dangers of defensive programming
Layer Anti-Pattern - the problems of a common, obvious approach


Agile

Principles

Agile's Fifth Element - favor simple design over re-usability and generality
JIT (Just In Time) - an example of DIRE that is core to much of Agile
DIRE (Don't Isolate Related Entities) - how you divide and conquer is the key
Agile Design - evolving software one small step at a time
Agile and Code Reuse - all about YAGNI (you ain't gonna need it)
Software Quality Assurance & Agile - how Agile evolved from, but is different to, SQA
Lean is not Agile - applying "eliminate waste" to software design leads to BDUF
Software Development Methodologies [CP] - Agile and other methodologies by analogy

Team

Scrum Team Size - teams should be small to avoid social loafing and other phenomena
Scrum Team Composition - "feature" teams are the key
Collaboration - traditional development discourages collaboration + why Scrum works

Making Agile Work

Scrum Standup - it's more about visibility than communication
Developer Quality Attributes - what benefits developers eventually helps users
Agile Version Control - Agile requires the right version control practices & software (Git)
Scrum Problems - management "buy-in" & other things that help Scrum work properly
Why Scrum Fails - intransigence, non-collaboration, etc
Written vs Verbal - when, who, why, and how of Agile documentation
JIT Testing - testing as you go (continuous testing) is an example of JIT (Just In Time)

Unit Tests

Change - how Unit Tests help you to embrace change
What's so great about Unit Tests - Unit Tests are not about finding bugs
White Box Testing - the best Unit Tests use "good" white box testing
Personal Experiences with Unit Testing - it took me 20 years to truly appreciate them
Challenges - why getting started with Unit Tests seems, but is not, insurmountable
Unit Tests Best Practice - a few things to avoid
Arguments Against Unit Tests - common arguments and why most are invalid
Summary - Unit Tests concisely summarized


Coding

Essentials

Zero - bugs are less likely if you don't treat zero as a special case
Asymmetric Bounds - in code and GUI design this is an important way to avoid bugs
Book Review: Clean Code - a great book on creating the best code

C Coding

Best Practice in C for Modules - strong-coupling and other things to avoid
Defensive Programming - how it works and how it can hide bugs
Shotgun Initialization - a defensive programming practice to avoid
Alignment and #pragma pack - make structs "alignment agnostic" to avoid surprises
Making Code Testable - coding for testability improves correctness, reliability, etc
Ten Fallacies of Good C Code [CP] - 10 more things to avoid

C++ Coding

STL's Dark Secret - vectors are slower than they should be
Iterators Through the Looking Glass - subtleties of the STL reverse iterators
C++11 and Lambda Functions - lambda functions make STL so much better
Nested Functions using Lambdas - you can finally have nested functions in C++11

C# Coding

Overflow Checking using checked/unchecked [CP] - C# has some cool features
Nested Functions using Lambdas - includes an example of using C# lambdas

Maintainability

Long Identifiers make Code Unreadable - don't try to put too much info. into a name
Self Describing Code - why it's a bad idea and why you should comment your code

Other

The Phillips Scale of Code Quality - how good is your code?
Version Control - Personal Experiences - hands on version control