Welcome Guest! To access all forums & features, please register an account or sign-in. → Why register?




Photo - - - - -

C#: why the best language still sucks

It's no secret that I love C#. I believe that in this day and age, C# is, 1) overall and 2) for the majority of development needs, the best solution. Let me clarify my thoughts about 1) and 2):

1) C# doesn't win in all areas against every language. It doesn't match Python in beauty, C++ in control and efficiency, Java in cross-platform support, etc. But all these languages heavily compromise somewhere else. C# doesn't make any extreme trade-off: it's fast, without the being the fastest; it's reasonably clean, while staying familiar to C++/Java programmers; it's compatible across a good variety of platforms, while providing superior Windows support, useful since Windows is still so prominent today.

2) I'd venture to say that for the majority of software, both programmer productivity and performance are important. There are times where productivity >> performance, where Python may be an even better choice; or times where performance >> productivity, in which case C++ may be the only solution. But in the vast majority of cases, we have very limited budgets to pay our programmers, and we still want the fastest software they can build with the limited time and funds we can pay them.

C# fits the bill perfectly: as its specification states, it aims to be "economical with regard to memory and processing requirements", while recognizing the importance of "software robustness, durability, programmer productivity", "source code portability", "internationalization", and be suitable for "the very large (...) down to the very small". I think C# has the right philosophy behind it: it's pragmatic. Anders Hejlsberg has always understood (as his work on Turbo Pascal and Delphi shows) that a useful general-purpose tool is one that doesn't epitomize one ideal over everything else.

C# is also much more than a relatively well-designed language, because there are many others. For example, Cobra is probably a better language than C#. But no one knows about it, because the only thing Cobra has to speak for itself is a compiler and a half-assed (more like quarter- or tenth-assed) Visual Studio plugin, while C# has full Visual Studio integration, that works well out of the box, is not too buggy, has good documentation, tons of support to be found on stackoverflow.com, books, etc. Today, a naked compiler is nothing; a good language is also the ecosystem around it and its online community. I wouldn't be half as productive as I am with C# if I had only a compiler and a support forum with 3 members.

That said, the fact that C# is the best we have today leaves me rather bitter, because see, C# sucks in many ways. It sucks less overall than others, but there's still a lot to be desired.

1) IDisposable. The main problem with a garbage-collected-only language like C# is deterministic resource management. Garbage collection manages memory very well, but there are tons of other resources: handles to various operating system objects (files, sockets, etc.), blocks of manually allocated memory (often needed for interop with native code), and more generally, being able to perform some cleanup operation when you're done with an object. The solution Microsoft came up with was IDisposable and the using pattern, which sucks because a) implementing IDisposable is a breaking change, b) the Dispose pattern is extremely touchy and difficult to understand, and c) the onus is still on the consumer of the class to ensure Dispose is called. Stephen Cleary wrote an excellent article on the topic.

2) Fail constructor/virtual semantics inherited from C++/Java. Why does this code compile without warnings? It doesn't even make sense! Yet, it's very easy to miss the problem without explicitely looking for it. And this code sample is approximately the smallest that reproduces the issue, so imagine how easy this is to miss in a larger code base.

class Base {
	public Base () { SayHello(); }
	protected virtual void SayHello() {}
}

class Derived : Base {
	string m_hello;
	public Derived() { m_hello = "hello!"; }
	protected override void SayHello() {
		Console.WriteLine(m_hello.Length);
	}
}

var d = new Derived(); // throws NullReferenceException ?!
I won't explain what the problem is, because the fact that many people don't immediately see what's wrong here illustrates the problem perfectly. If you really don't see it, paste the code in Visual Studio and run it line-by-line in the debugger.

I'm no Heljsberg, but I'm sure there are ways to achieve what constructors and virtual methods achieve while making this scenario impossible. Keep in mind, I'm not saying this code should behave differently; I'm saying a good language is one that doesn't compile nonsensical code (semantically, not logically, of course).

3) Poor syntax inherited from C. C has an egregious syntax for for loops and switch statements, among others, yet for some reason C# (and many others!) inherits it. It's not just the clumsiness and the amount of typing necessary, it's mainly that these constructs are error-prone.

// What is the problem here?
var array = new int[20][];
for (int i = 0; i < array.Length; ++i) {
	array[i] = new int[20];
	for (int j = 0; i < array[i].Length; ++j) {
		array[i][j] = i * array[i].Length + j;
	}
}
If you're looking for it, this is easy to spot; obviously the second loop's end condition should compare j, not i. But since I'm a human and I'm not always paying attention, this kind of error sometimes slips by, and the compiler cannot help me, since it's perfectly valid code. Consider that simply changing the syntax makes this error impossible, at no cost in performance or expressiveness:

// VB.NET (much better):
For i = 0 To array.Length

// Cobra (even better)
for i in array.Length

4) No non-nullable references. You won't hear me say this often, but C++ has C# beat on this one. In C++, you can declare a reference that is statically provably not null (T& myObject), and proving something statically is always infinitely better than sort-of-but-not-really proving it through unit tests or code contracts. This concept is easy to implement and would rule out a whole class of NullReferenceExceptions, and NullReferenceExceptions represent probably 50% of all failures of .NET programs!

Anders Hejlsberg himself has admitted this as something he regrets not putting in, and today it is too late to add it to the language. :( (See Anders Hejlsberg: Questions and Answers at 1:00:35)

5) Poor generics support. It's better than Java, mind you! At least the genericity is not magically erased at run-time. But when you read about generics, the first thing you want to do (assuming like me, you like writing games) is this:

class Vector2<T> {
	 public T X { get; set; }
	 public T Y { get; set; }
	 public static Vector2<T> operator +<T>(Vector2<T> a, Vector2<T> b) {
		 return new Vector2 { X = a.X + b.X, Y = a.Y + b.Y };
	 }
}
Oops, operator + isn't defined for T. Well, constraints should solve that, right? I should be able to say:

class Vector<T> where T : operator+
or
class Vector<T> where T : Numeric
Well, no you can't. Turns out the only thing you can put in that "where" clause is "new()" (has a parameterless constructor), "SomeType" (implements or derives from SomeType), "class" or "struct" (is a reference or value type). The very common scenario where you want to make an algorithm valid for any numeric type isn't supported, because there's no way to specify: "T is a numeric type".

Mind you, this can be side-stepped by defining interfaces with "Add()", "Multiply()" methods etc., but that's a poor alternative! Jeffrey Richter, in CLR via C#, notes the same thing: "This is a severe limitation on the CLR's generic support, and many developers (especially in the scientific, financial, and mathematical world) are very disappointed by this limitation. (...) Hopefully, this is an area Microsoft will address in a future version of the CLR and the compilers."

It goes further than this, however. Another major issues with generics is that they didn't make it for version 1.0 of the framework, and as such, much of the class libraries are designed around having no generics. Some libraries had new, generic versions when 2.0 shipped (notably System.Collections.Generic), but most did not. A lot of subtle issues crop up, a typical one being:

foreach (var e in Enum.GetValues(typeof(MyEnum))) {
	if (e == MyEnum.Val1) {} // doesn't compile because type of e is object
}

// What would be if .NET 1.0 had had generics:
foreach (var e in Enum.GetValues<MyEnum>()) {
	if (e == MyEnum.Val1) {} // compiles !
}

I think that sums up the big picture issues with C#. I have of course other pet peeves, like the fact that LINQ-to-objects is too slow to really replace for loops in general, or that floating-point performance sucks (although that's more of a CLR issue), etc, but these could be addressed relatively easily by Microsoft, if they wanted to.

To conclude, C# may be the best we have right now, but we should hope and ask for something better. If you haven't, try out Python, Scala, Cobra, F#, see how they do things differently, see how they change the way you think about programming, and how these ideas could make it into C# or some successor of C#.

Unfortunately, Microsoft is more busy catering to users of horrible languages (Javascript integration in Windows 8 is particularly vexing) than trying to push them towards greener pastures. I understand the financial motivation, but it's sad.

As a side note, the current Microsoft enthusiasm for C++ worries me. I've watched every Herb Sutter talk where he tries to explain why "performance" is suddenly so critical in 2012, but that doesn't explain why there are no signs of a new version of XNA. Or why they don't fix WPF performance problems if performance is so important (isn't WPF written in C++ anyway? That should make it fast, right ? ;) ). I fear Microsoft is shifting its focus from a beautiful framework that could use some love to a horrid mishmash of under-specified, overly-complex features piled on top of a brittle, dangerous memory model (Eric Lippert, principal designer of C#).



Great read :)

About non-null references, you could try Spec# which extends C# with non-nullable references (in fact, references are non-nullable by default in Spec#), code contracts (requires/ensures/modifies/... like Cobra, it seems) and stuff. I read the spec but never tried it, though - it looks like it was made to create statically provable code, which is very nice but a PITA to write since methods, properties, even loops must say exactly what they do and what guarantees they offer.

Aethec, on 09 April 2012 - 10:58, said:

About non-null references, you could try Spec# which extends C# with non-nullable references (in fact, references are non-nullable by default in Spec#), code contracts (requires/ensures/modifies/... like Cobra, it seems) and stuff.
Thanks, I'll keep your suggestion in mind.
Definitely found the article interesting. Although the c# language allows you to use plain for loops, what about the foreach loop? I think the problem is that it doesn't allow you to intuitively operate on a two dimensional array. I am curious: can you use the same iteration you gave in your examples for VB and cobra to traverse a two dimensional array?

Gremzor, on 10 April 2012 - 16:20, said:

Definitely found the article interesting. Although the c# language allows you to use plain for loops, what about the foreach loop? I think the problem is that it doesn't allow you to intuitively operate on a two dimensional array. I am curious: can you use the same iteration you gave in your examples for VB and cobra to traverse a two dimensional array?
You often need a counter in a loop, and that's when you need the traditional for rather than foreach. Yes of course you could the same for cobra and VB, for some reason though I can't find the syntax to create a two-dimensional array in cobra, but it'd be something like:

for i in array.Length
  for j in array[i].Length
	  element = array[i][j];
in VB:
For i = 0 To array.Length
  For j = 0 To array(i).Length
	  element = array(i)(j)
  Next
Next

I know that cobra actually has a combined for-foreach loop that both iterates the collection and gives you a loop counter, but it's been a while since I tried it.
Good article. I remember looking at Cobra a few years ago and liking what I saw. It's a shame it seems to be a dead language now.
Poor cross-platform support is no go for me. If all you develop for is Windows based OS's I'm sure it's fine, but I need to write code consistently across platforms, which is why I like C, Java, Python, Perl, and a few others. I'm never going to lock down my development to one OS and one proprietary API. That's the surest way to become irrelevant.

I also need vim, a gdb debugger, the GNU auto tools etc to make an efficient development/build environment. Visual studio might look pretty if you're into the eye candy, but to get any real work done, I need a terminal based environment.

simplezz, on 17 April 2012 - 18:07, said:

Poor cross-platform support is no go for me. If all you develop for is Windows based OS's I'm sure it's fine, but I need to write code consistently across platforms, which is why I like C, Java, Python, Perl, and a few others. I'm never going to lock down my development to one OS and one proprietary API.
I'm not sure if that's an attempt at trolling or not. C# runs across every browser and operating system today, and if anything, is much more alive than any of the languages you mentionned. C#, the CLI and much of the .NET framework are Ecma standards. ASP.NET is open-source.

Everybody knows that... right?

Dr_Asik, on 18 April 2012 - 00:46, said:

I'm not sure if that's an attempt at trolling or not. C# runs across every browser and operating system today, and if anything, is much more alive than any of the languages you mentionned. C#, the CLI and much of the .NET framework are Ecma standards. ASP.NET is open-source.

Everybody knows that... right?
He said poor platform support, not that there is none. That means you'd have to rely on Mono under Linux. Can you honestly say that the cross-platform support for C# is as good as the languages he listed?

Xinok, on 18 April 2012 - 03:42, said:

He said poor platform support, not that there is none. That means you'd have to rely on Mono under Linux. Can you honestly say that the cross-platform support for C# is as good as the languages he listed?
C# is certainly supported better than either Perl, Python or even Java to make cross-platform iOS/Android apps, nevermind WP7. And it certainly has nothing to envy to these languages on Mac OS and desktop Linux. Is there any equivalent of Unity, the Delta Engine or even MonoGame for developing cross-platform games in C, Python, Perl or Java?

Mono is a fine technology that has been actively developed for as long as .NET has existed, it has evolved, with .NET, at a much faster pace than any of the other technologies listed, and it is going nowhere. It even surpasses Microsoft's .NET in a few areas (it supports SIMD instructions for instance).
Hi, I'm the author of the Cobra programming language.

@Xinok, it's not dead. I was just taking an RSI break, that's all.

@simplezz, I do most of the Cobra compiler work on Ubuntu+Mono. I also test on Mac and Windows and do some development there as well. Also, we have a JVM back-end under way, though we could use more volunteers to help out. (Cobra is a community driven open source project.)

@Dr_Asik, we could use some help on the Visual Studio plug-in. You seem passionate about it. ;-)

Anyway, I have been polishing a few things on the compiler. After that, the web site needs some love. Then a new release this summer. But you can see activity right now on the discussion forum and the Trac timeline.

Thanks for your interest.

CobraCommander, on 10 June 2012 - 09:57, said:

Hi, I'm the author of the Cobra programming language.
Thanks for passing by! Did you find the article by searching for Cobra in Google or... ? Anyway I'm glad to know the project is alive and well.

I wish I had the time and knowledge to improve the Visual Studio plug-in...

CobraCommander, on 10 June 2012 - 09:57, said:

@Xinok, it's not dead. I was just taking an RSI break, that's all.
Sorry to hear about your injury, but happy to hear that Cobra is still alive. :) I'm very much a supporter of D, but I appreciate any programming language with good design, so I'd be happy to see Cobra live on.

Dr_Asik, on 11 June 2012 - 23:51, said:

Thanks for passing by! Did you find the article by searching for Cobra in Google or... ?
Just guessing: http://www.google.com/alerts
@Dr_Asik, in answer to your question, I was googling for mentions of Cobra. @Xinok, I do have Google Web Alerts going, but don't recall getting one for this page for some reason.

So the reason I stopped by was to share that Cobra 0.9 has been released. I won't post further release announcements in these comments so if you're interested you'll need to check the site or subscribe to the freecode entry.

If you have general questions or feedback, please use the Cobra discussion forum. Thanks.

May 2013

S M T W T F S
   1234
567891011
12131415161718
19 202122232425
262728293031 

Categories