Monday, 26 September 2011

From C++ to C# – Two Years On

Two years ago I interviewed for what was originally a C++ position, but which for internal reasons switched to being a C# role at the last minute. I was still offered the contract even though I knew virtually no C# with the expectation that I would pick it up pretty quickly as the two languages aren’t exactly worlds apart. After doing 15 years of C++ I was somewhat pensive of switching my main language, but as I wrote back then in “Why is C++ Getting Left on the Bench” the market for C++ is slowly diminishing. So of course I took it!

Comparing languages is somewhat of a dubious pursuit as the reason we have so many is exactly because they are intended to solve different types of problems. However these two cover similar ground and I’ll be using them to solve the same kinds of problems so I think it’s probably an interesting comparison on some level...

Tooling

I’ve only ever worked on Windows and Visual Studio has pretty much been the only IDE I’ve used for the past 15 years, so naturally I was instantly at home with the default toolset. The only big difference I’ve noticed is that IntelliSense actually seems to work with C#, unlike C++[#]. However our system is still quite small so it’ll be interesting to see how that scales. Build times are also pretty good, but the same caveats about project size apply.

I’m sure this is obvious to any seasoned C# programmer but the biggest win seems to come from Resharper[*], a plug-in that provides on-the-fly static code analysis to point out potential bugs, stylistic violations and all round good practices. This tool just makes the refactoring step in TDD that bit more pleasurable and easy. There is a tool in the C++ world vying for similar acclaim (Visual Assist) that I used some years ago on another contract and it does add value to Visual C++, but I suspect that C++ is just so much harder to parse than C# and so I doubt it will match something like Resharper.

Something you should bare in mind that I only write services and so have no use for all the GUI related features. After all, I still code the few html pages I do write the way I’ve always done - with a text editor.

Libraries

Resharper aside the other big win has been the bundled libraries. C++ gives you a basic set of libraries to cover simple I/O needs, string formatting etc, but avoids giving you the other things a modern developer needs - networking, xml support, threading. Ok, so this is being rectified to some degree in the new standard and Boost can fill in a huge number of gaps in the other areas - but fundamentally, out-of-the-box, you’re on your own. Being able to do so much more from the get go means the project can progress in many areas without getting bogged down in technology problems quite so soon.

Of course Windows and .Net come from the same stable and so any notion of being “cross-platform” (Mono notwithsdtanding) is a drop in the ocean compared to the vastly differing platforms that C++ aims to support. But as you should already have gleaned by now that is of little interest to me, at least professionally.

Language

At a basic language level there are so many similarities between the two, constructs such as classes and enumerations are roughly equivalent and the private/protected/public access levels are pretty close too. Yes there are various small notational differences, such as using a . instead of :: to separate namespace names, but I’ve covered this in an earlier post “C# + C++ + C++ CLI – Context Switch Hell”. I’ve also documented my comments on resource management in the past too in “IDisposable’s Should Assert In Their Finalizer”.

This means that by-and-large I’m designing and writing code in a very similar manner. But then that was to be expected as the expression of OO is through interfaces and classes and that was never going to be much different. However the inclusion of Closures (which C++ has recently gained too) has meant the Functional Programming element probably feels more natural now - Functors and Boost::Function are just very clunky mechanisms in comparison.

There are many small touches that make certain common C++ pitfalls a thing of the past, but most seasoned C++ programmers have long since learnt how to avoid making them. There are a couple of things in C# though that are still bugging me:-

Sorely Missing Const

The thing that I miss the most by far is the concept of “constness” in C++. In C# immutability is a property of a type (assuming you choose to design the class that way), not a trait of an instance of a type. One place where this really comes into play is when concurrency rears its head because immutable instances are shareable across threads. Another is when dishing data out from a cache - you want to return a const reference to ensure the instance is not accidentally modified. I have written a number of bits of code in C# already where I know that if a cache was introduced the compiler would not be able to help highlight the violations - that scares me[^].

It didn’t take me long to notice the loss of constness, but that may be largely due to me regressing to old habits such as returning a const reference to an internal container rather than exposing the functionality on the type (the proverbial Law of Demeter), or exposing iterators via begin()/end() methods (ala Extended STL). In C# IEnumerable covers similar ground and more besides, so careful thought eliminates many cases in the end.

Don’t even mention ReadOnlyCollection! That is an abomination. I’m using a statically compiled language because I want the compiler to point out my mistakes at compile time, I don’t want to rely on it happening at run time. Yes, good unit test coverage does minimise the chance of a bug slipping through the net, but I don’t understand why there isn’t an IReadOnlyList, from which IList derives?

My current system uses a complex in-house container type that can be very expensive to copy and it too provides a single interface for reading and writing. The later addition in C# of the “initialiser syntax” seems to actively promote the creation of mutable types by default[+] which is not the de facto stance to take when you want thread-safe code to be the norm and not the exception. Even if you’re not bothered about immutability, this syntax still relies on you supporting 2-phase construction which is another thing to avoid by default.

Equality Comparison is Hard

In C++ everything is essentially a value type and so comparison is simple to implement. If you try and implement it on a type and anything it aggregates doesn’t support it you’ll get notified at compile time. C# is the other way round, which is both a blessing and a curse. Manually implementing the Equals() method correctly is non-trivial and that’s why everyone uses a tool like Resharper. Steve Love and Roger Orr gave an excellent talk on the subject at this year’s ACCU conference which is what made me realise the subject is way deeper than I ever imagined.

So what’s the blessing? Well it’s caused me to try and properly understand the difference between value types and reference types. This is turn has forced me to question whether I should be providing a comparison operator on a type in the first place or am I just doing it because it used to be easy. Unit testing makes this question harder because you often see a need for comparison as your unit test asserts are usually implemented by comparing the actual and expected outcomes. Implementing Equals() once in the type seems easier, but you can always implement it as a test helper method instead and refactor later if it turns out you got it wrong.

Productivity

So do I feel more productive in C# than in C++? Probably. Slightly.

I’m one of those people that believes most time is spent in the designing phase, not in the actual coding. I spend way more time thinking about the problem and when the moment comes I probably write it in much the same way in either language. I’ve mentioned how nice it is to have a decent set of libraries on tap, and we definitely managed to leverage that in the early part of the project, but once the infrastructure was written it was pretty much business as normal.

If anything, the biggest win so far has been the tight integration between PowerShell and .Net as that has provided us with an ability to work around production issues you could only dream about in the days of C++/COM/VBScript.

 

[#] Throw something macro heavy like STLport into the mix and IntelliSense seems to fall apart.

[*] Nope, I have no relationship with them whatsoever, just a happy [corporate] customer.

[^] After what I said about TODOs the other day, this is one example where I have turned turned a TODO into a formal non-functional requirement. This is because the code is dangerous and building a facade over the mutable type in question is non-trivial, as is creating copies.

[+] The C# initialiser syntax relies on using public setter methods to assign values to properties. Curiously, when creating an anonymous type, you use the same syntax, but the compiler does not generate the public setters. Of course the new named argument syntax in C# 4 will hopefully push people back onto the right course once again.

3 comments:

  1. "I don’t understand why there isn’t an IReadOnlyList, from which IList derives"

    That wouldn't be enough. If you have an IReadOnlyList you can always cast it to IList.

    Unless IReadOnlyList is an interface on a different class that is returned as a proxy - but then you are back to ReadOnlyCollection again.

    It really is an intractable problem without language support (although const isn't perfect in C++ either).

    ReplyDelete
  2. Jon Skeet mentioned another interesting tool just recently on twitter:

    "Just started using NCrunch (http://www.ncrunch.net). I think this is the way forward... really easy to get going with, too..."

    NCrunch is an automated parallel continuous testing tool for Visual Studio .NET.

    Cheers, Martin

    ReplyDelete
  3. @PhilNash

    "That wouldn't be enough. If you have an IReadOnlyList you can always cast it to IList."

    Is that any different to using a const_cast<>() in C++? As long as there is no implicit conversion available that would be a start.

    You can apply the same logic to immutable types, which become mutable via reflection. Deliberately subverting the type system takes you off-piste in my opinion.

    ReplyDelete