A Critique of the C# Programming Language

I managed to open and skim through the O’Reilly book “Programming C#” (2nd Ed.), by Jesse Liberty. I won’t pretend to approve of the language or the agenda that brought it into being.

I have no access to a C# compiler, so I can’t run experiments, and so, this all has to be taken with a grain of salt.

One objection to a “critique of C#” would be that you can’t talk about C# without talking about the whole “.Net experience”. However, one can approach the topic of Hitler without a complete discussion of Nationalist Socialism, so I feel justified.

The book, and Microsoft’s web site, highlight similarities between C# and C++. No independent programmer familiar with these languages would see C# as a descendant of C++, however. Plainly, C# is a Java knock-off, with many additional elements from C++ and scripting languages such as Visual Basic thrown in. I’m going to approach this primarily by comparing C# to Java.

I’ll spend time discussing the “.Net” features of the language, for two reasons. First, these are the features of C# that are most ballyhoo’ed, and second, I’m not personally very familiar with the analogs of these features in other languages.

Overview

The syntax of C# almost contains Java as a subset. Much straight Java code would compile without alteration; almost all would compile with a few syntactic alterations but no structural changes. The major syntactic differences are: class declarations look like C++ declarations, the keyword virtual is used to label methods as overridable in subclasses (in Java, methods are virtual by default) and the following keyword differences

C# Java
sealed class final class
readonly (member) final member
using import (+using?)
is instanceof

Otherwise, like Java, C# has native threads, garbage collection, and exception handling. It does not use header files. As in Java, there is no such thing as a function outside a class in C#. As in Java, all C# classes derive from class Object, and primitive types (int, float) have fixed bit definitions.

This language is huge—substantially bigger than C++. The book lists 80 keywords. (Microsoft’s site lists only 77, but it excludes get and set—(why?)) The Java 1.4.1 Language Specification lists 48, a couple of which are unused. I have seen a list of 49 for C++. (I can’t find a list of keywords for Perl—there are so many; perhaps because it’s difficult to distinguish a keyword from the many syntax oddities in that language. )

language version keywords notes
C# 80 according to the book
C++ 49
Java 1.4.1 48 (two are unused) Java Language Specification
C ANSI 32 K & R
Python 2.2 29 Python Reference Manual

There are a few nice constructs in C#—even a few things I’ve missed in Java. It looks to me like a usable programming language, if you can find drugs to fight the gag reflex.

As Java runs in a certain restricted environment (a Virtual Machine, or VM) that could exist on any computer-like device, C# may be used to produce applications on any device that supports the Common Language Runtime (CLR). The CLR has one advantage over the Java VM in that it is designed to be language-independent. The Java VM presently runs on a wide range of hardware and operating systems. My guess is, the CLR will never be so widely spread—not because it couldn’t be in principle, but because such accommodation isn’t Microsoft’s style.

As in Java, there is no multiple inheritance in C#. Although the C# class declaration looks like the (relatively unsuggestive) C++ one, it is functionally identical to the Java declaration. You extend from no more than one class, but can implement multiple interfaces.

As in Java, C# types are divided into two main genera, value types and reference types; value types being types such as int, float, and so forth, and reference types being objects. Value types are passed to functions by value, reference types by reference.

The implementation of polymorphism (method overloading) differs between Java, C++ and C#. Here, C# has a small advantage of providing greater specificity (although it takes more code and more keywords to achieve it). You have to explicitly mark methods as virtual (as in C++) but then you have to also mark subclass methods as overrides. This might eliminate certain annoying situations that happen in C++ and Java, such as shadowing. To create a subclass method with the same signature of a superclass method, but which doesn’t override the superclass method, you mark it with the keyword new.

C# also allows member data to override inherited data, using the new modifier. I can’t imagine how this is a good thing.

The primary inadequacy the Java language as a large-scale applications programming language is that absence of a convenient means of controlling responsibility for altering an object. This is done in C++ with const. This crucial feature is also lacking in C#. (As near as I can tell, the keyword const in C# is a synonym for readonly, just a modifier for member data. So you can’t say which methods of a const object can be called, etc.). I know of no other feature of C# that fills this role, so I have to pass the same judgment on C# as I have with Java: not appropriate for large-scale applications programming. This isn’t a blanket condemnation—not all applications programming is so large-scale, and in particular, most Web-delivered applications shouldn’t be.

.Net

MetaData

.Net programs can contain metadata, which is information that can be read in a standardized way from outside. This is reminiscent of the Mac “resource fork”.

C# provides a means of inserting metadata directly into the code. Here, it can double as a comment, or affect how the code is compiled. This is done using attributes, which coded as text between square brackets.

Common uses of attributes:
[Serializable] — makes following class savable to disk
[WebObject] — makes following code visible by .Net apps

Reflection

Reflection is the capacity of an object to investigate its class information: its parent class, its class hierarchy, its methods and their signatures. In C# can also look at metadata.

C# reflection is similar to that provided by the Java reflection library.

C# Reflection Emit allows dynamic creation of new types. This is completely absent in Java (although it is rumored to be planned for Java 1.5.1). In C++, templates serve part of this role. This sounds useful to me. If the syntax is nicer than C++ template syntax, it’s going to be a good thing.

As near as I can tell, this permits C# code to be constructed, compiled into a module, which is loaded and run, on the fly, from a running program. Holy moley! Is that a good idea? The book has a concocted example of how it can provide more efficient code in specific cases, but the overhead of doing the emit would typically be prohibitive.

Webobjects

By tagging a method with the [WebObject] attribute, you make it so that a web application can see and call that method.

You can then write another application that runs remotely, but which is aware of the WebObject method, and can call it and get return values. Communication can use the old CGI get and post calls, or the more modern XML protocol SOAP, which has much better type support.

Assemblies

A module is the CLR analog of a library. If saved individually to disk, they are saved as DLL’s.

An assembly is the CLR analog of an application. Assemblies consist of modules. On the disk, assemblies are saved as Portable Executable (PE) files.

Each assembly has exactly one entry point: WinMain for Windows apps, Main for DOS apps, DLLMain for DLL’s.

CLR assemblies and modules contain a standardized version and manifest.

The version is a number which is shared by all parts of the assembly. All parts of the assembly must be updated together. You can’t have a higher version number in one module in the assembly than in another. The book sneers at the idea of shared libraries, saying they are nice in theory, not in practice, and denies that backward-compatibility can really work.

The manifest lists all the parts of the assembly, describing methods and data. It is described as metadata in the assembly.

Syntactic debts

From C/C++

enums
structs
unsigned types
preprocessor instructions
goto
do…while
“destructor” syntax looks like C++ but acts like Java finalizer
operator overloading
pointers and pointer arithmetic
have to declare unsafe mode—only for native libraries and code

From scripting languages

switch on string
foreach
as (same as is plus a cast)
dynamically add elements to arrays

Reminiscent of Simula

argument modifiers ref and out for pass-by-ref and initialize-by-ref

Maybe unique to C#

internal access modifier
Restricts access to the current program (protected internal limits access to subclasses in this program.
boxing
Conversion of value type to reference type.
	int i = 48;
	object	obj = i;	// boxing is implicit
	int j = (int)obj;	// un-boxing must be explicit 
delegates
Like C++ function pointers, but type-safe.
	delegate void D( int x );
	class A {
		public static void F( int i );
	}
	class B {
		void foo() {
			D myF = new D( A.F );
		}
	} 
properties
Allows private member access to look like public access, but really calls accessors of a contained class. To do it, create a private member variable, then create a public contained class, say, Foo, with two methods, get and set, which access the member variable. Then can use Foo as though it were a member variable: obj.Foo = 1. Is this a good thing?
override; virtual is root of polymorphism
The last virtual method with a given name in a class is the root of polymorphism for that method. To override the functionality in a subclass, must explicitly mark as override. (Is virtual override possible?)
variable-length arg list
void foo( params int[] val )
virtual member fields
reflection emit
see below

C++ features not in C#

templates
there is some talk about including this in a future release
multiple inheritance
functions and variables outside of classes
const-ness
The const keyword is part of C#, just not discussed in this book (as near as I can tell). At the MS site, the examples only have it a a sort of synonym for readonly.
bit fields
method parameter default values
fall-through in switch
initialization lists
Neither C# nor Java require C++ constructor initialization lists. They each provide a special mechanism for initializing the parent class with a particular constructor, though.

Comparison of inheritance and polymorphism

C++

	class A {
		public:
			A( int x )
			{ }
			virtual ~A() {}
			virtual void amethod(){}  // virtual methods must be so marked
	};
	class B {	// C++ doesn't have interfaces per se
		protected:
			B() {}
		public:
			virtual void bmethod();
	};
	class C : public A, public B {
		public:
			const int	y;

			// const and reference members must be
			// initialized in constructor initialization list
			C( int x, int y0 )
				: A( x ),	// parent class constructor
				y( y0 )
			{ }
			virtual ~C() {}
			void amethod(){}
			void bmethod(){}
	};

C#

	class A {
		public A( int x )
		{ }
		virtual void amethod(){}  // virtual methods must be so marked
	}
	interface B {
		void bmethod();
	}
	class C : A, B {	// note everything after comma must be an interface
		readonly int	y;

		public C( int x, int y )
			: base( x ) {	// parent class constructor
			// readonly member initialization required by
			// syntax in every constructor
			this.y = y;
		}
		public override void amethod(){} // explicitly mark overrides
		virtual void bmethod();
	}

Java

	class A {
		public A( int x )
		{ }
		public void amethod(){}  // methods virtual unless 'final'
	}
	interface B {
		void bmethod();
	}
	class C extends A implements B {
		final int	y;

		public C( int x, int y ) {
			super( x );	// parent class constructor
			// final member initialization required by
			// syntax in every constructor
			this.y = y;
		}
		public void amethod(){}
		public void bmethod(){}
	}

About the book

The index is useless. For instance, the only reference given in the index to many C# keywords points to the keywords appendix, which provides a brief description, but doesn’t give further references.

It is apparent that C# is a language that needs a whole shelf of books to describe with precision. Besides that, the book should serve as an adequate introduction.

References

I did some web searches for other critiques of C#. By far the most common result was a glowing recommendation, typically on the basis of the superiority of C# over Visual Basic. (“It’s so much better than Visual Basic—it’s got objects and everything!”)