The history of modern computing is paved with good intentions and great intuitions. At different times, all the players in the arena brought excellent intentions and brilliant ideas to the table, but with different results. Microsoft had success with Visual Basic (VB) and Active Server Pages (ASP), but the company was less fortunate with ActiveX. Sun Microsystems pushed Java as the savior of the computing world, but, despite its brilliant architecture, the language fell short.
From Java to C#
Java's unquestionable merit is that for the first time, you (and your compiler) don't have to know the details of a particular CPU to generate executable code. Java introduced an intermediate layer of compiled code, called bytecode, that applies to a virtual, abstract machine instead of a real one. Once a Java compiler finishes with a source file, what you have isn't code that you can immediately run on a given platform; instead, it's bytecode that you can run on any real platform that understands and supports Java. These developments constitute a cultural revolution. As a developer, you simply target the abstraction layer that the Java Virtual Machine (JVM) provides, leaving the various OS vendors to implement the layer of code that maps the platform-neutral bytecode to the host platform's architecture. In this scenario, Java is seemingly the leading candidate for uniting the world of distributed computing. "Write once and run forever" (and wherever) was once Java's somewhat intriguing, yet true, slogan.
So why is Java just seemingly a good thing? The theory of platform-independent development is well proven. Unfortunately, porting Java code from platform to platform—the language's most important and touted feature—hasn't been as easy and effortless as promised. Any Java-enabled platform has its own virtual machine that understands the universal bytecode and compiles it just-in-time (JIT) into native code. The rub is that the various implementations of virtual machines differ quite a bit—enough to make porting the code much more time consuming than expected, and hardly automatic.
So, what's good about the Java model? First, Java is a modern, object-oriented language that includes built-in features to prevent common errors and that brings a lot of frequently requested functionality within just one or two objects from you. Compared with C++, Java is easier to read and write, less error prone, and inherently more elegant—but it's slower and less flexible. To be portable to virtually any software and hardware platform, Java uses the least common denominator model, which means that by design, you give up the ability to exploit each platform to its fullest. Second, the idea of a virtual machine is inherently portable and interoperable, and therefore ideal for distributed environments. Java is a great language for developing code for non-Windows platforms.
What about Java for Windows? Adapting Java to Windows isn't possible because of Sun's licensing restrictions. Of course, you can rewrite Java—but at what price? Microsoft needed a great pretext to unveil the "perfect" language for Windows, and so it took the best of Java and made it specific to Windows. The result is a surprisingly simple, elegant, powerful, type-safe, OOP language called C#. C# outperforms C++, it can natively import all the classes of the .NET Framework Class Library, and it simplifies the syntax.
C#'s Main Features
C# is the key language of .NET. I dare say that it's the basis of the entire .NET platform. The other languages that .NET supports today inevitably pale in comparison with C#. VB.NET exists primarily as a nod to the thousands of VB developers. We can say the same about JScript.NET and Managed C++, which is the C++ language as we know it plus the ability to call into .NET classes. C# is the only language that hasn't been designed with some sort of legacy code in mind.
The .NET platform, with C# as its native language, revisits many Java principles. It has a virtual machine, called Common Language Runtime (CLR), and it has a similar hierarchy of objects. But C# is designed to expose the whole Win32 API and even more. Because it's close to the Windows architecture, C# will soon become very familiar to developers.
Unlike Java, which set out to save the world of distributed computing, C# is essentially an evolution of C++ that employs many C++ features, including statements, expressions, and operators. However, C# also features significant enhancements, such as type safety, event handling, garbage collection, and code security. With C#, you have access to a variety of APIs, aside from the .NET classes. In particular, you deal with COM automation and C-style functions.
C# also lets you call into unmanaged code, which is code that runs outside the control of the CLR engine. This unsafe mode lets you manipulate raw pointers to read and write memory beyond the control of the built-in garbage collector.
The Structure of C# Programs
A C# program is given by a class with at least one public static method, called Main. This method initializes and terminates the program. With the Main method, you create child objects, execute methods, and implement the logic of your software. The following is an example of a typical minimal C# program:
static int Main(String\[\] args)
System.Console.WriteLine ("Hello .NET");
Instead of #include directives to import external definitions, in C# you use statements such as the following:
The C# compiler, csc.exe, is part of any .NET installation. To compile the previous code example, which we'll assume that you saved as hello.cs, you use the following command:
The result is hello.exe, which writes "Hello .NET" to the console output window.
Despite the .exe suffix, hello.exe isn't a real, CPU-specific piece of code. In fact, it consists of .NET bytecode. When you launch hello.exe, the CLR extracts the significant metadata that the compiler wrote in the code. A module called Just-In-Time Compiler then maps that code to the specific CPU for actual execution. This process takes place on the fly and is completely transparent to you.
C# and Namespaces
Real-world C# programs typically consist of multiple files, each of which can contain one or more namespaces. A namespace is a name that represents a compiler for software entities such as classes, interfaces, enumerations, and embedded namespaces. Namespaces, as well as data types, must have unique names. You can always identify an element in a C# program by its fully qualified name, which indicates a hierarchy. For example, System.String is the fully qualified name for the .NET String type. However, as long as you declare that you're using the System namespace
You can just use a relative name (e.g., String) as a synonym for the complete name, which remains System.String.
You can wrap a C# program or class in its own namespace using the namespace keyword:
using System; // for String
static int Main(String\[\] args)
System.Console.WriteLine ("Hello .NET");
The namespace MyOwn is part of the global namespace. To call it, you don't use a prefix because its fully qualified name is simply MyOwn. Defining a namespace is a way to preserve the uniqueness of public names. In fact, two classes with clashing names are unique as long as they belong to different namespaces.
The VB Storm
If C# is a breath of fresh air in the dusty world of Windows, it's also a destructive storm for VB programmers. C# is the living image of the CLR—the .NET virtual machine. Everything running with the help of the CLR is managed code, and not raw Win32 executable. In light of this, VB.NET looks like a VB mask on the face of C#. VB as we know it today is dead. Long live VB.