C#: A language alternative or just J--?, Part 1

What the new language for .Net and post-Java Microsoft means to you

1 2 3 Page 2
Page 2 of 3
Table 3. Constructs common to Java and C#
StatementJavaC#
if/then/else
int i = 0;
if (i == 0) {
   i = 1;
} else {
   i = 2;
}
int i = 0;
if (i == 0) {
   i = 1;
} else {
   i = 2;
}
switch
int i = 0;
switch (i) {
   case 0:      i = 1;      break;
   case 1:      i = 2;      break;
   default:     i = -1;     break;
}
int i = 0;
switch (i) {
   case 0:      i = 1;      break;
   case 1:      i = 2;      break;
   default:     i = -1;     break;
}
while
int i = 0;
while (i++ < 10) {
}
int i = 0;
while (i++ < 10) {
}
do/while
int i = 0;
do {
} while (i++ < 10);

int i = 0;
do {
} while (i++ < 10);

foreach
import java.util.Vector;
public static int sum(Vector v) {
   int sum = 0;
   for (int j = 0; j < v.size(); j++) {
        Integer i = (Integer)v.elementAt(j);
         sum = sum + i.intValue();
   }
   return sum;
}
// Note: Java doesn't have "foreach".
// This method uses java.util.Vector
// to achieve the same end.
using System.Collections;
static int SumList(ArrayList theList) {
   int sum = 0;
   foreach (int j in theList) {
      sum = sum + j;
   }
   return sum;
}
break/continue
// Works with: for, while,
// do, switch
int i = 0;
while (i++ < 10) {
   if (i < 5) continue;
   break;
}
// Works with: for, while, do,
// switch, foreach
int i = 0;
while (i++ < 10) {
   if (i < 5) continue;
   break;
}
return
public void returnNothing() {
   return;
}
public int returnOne() {
   return 1;
}
public void returnNothing() {
   return;
}
public int returnOne() {
   return 1;
}
new
Something s = new Something();

Something s = new Something();

throw

try/catch/finally

try {
   throw new SampleException();
} catch (SampleException ex) {
} finally {
}
try {
   throw new SampleException();
} catch (SampleException ex) {
} finally {
}
// However...
try {
   throw new SampleException();
} catch { // Note argument optionality
// Not only is the argument to
// catch optional, but
// the entire catch clause
// itself is optional.
// Also, C# has no throws keyword.
} finally {
}

exclusive

access

synchronized(this) {
   // do something
}
lock (this) {
   // do something
}

class

definition

class Foo extends Bar {
...
}

class Foo : Bar {
...
}

interface

definition

interface IFoo extends IBar {
...
}

interface IFoo : IBar {
...
}

interface

implementation

class Foo implements IFoo, IBaz {
...
}
class Bar: IFoo, IBaz {
}

As you can see in Table 3, most procedural statements in C# are similar, if not identical, to their corresponding statements in Java, and both languages are very similar to C++. There are a few differences worth noting:

The foreach keyword: C# has a built-in construct, foreach, used for iterating collections. In Table 3, this keyword iterates over a collection of ints. The expression (int j in theList) defines an iteration variable j, which is subsequently assigned to the integer values of the array theList. If a value in theList cannot be converted and assigned to an integer, an exception is thrown; in other words, the iterated collection is not of a specific type, and therefore the foreach keyword is not type-safe at compile time.

Java lacks the foreach keyword, of course, so one possible implementation of collection iteration appears in Table 3, using java.util.Vector as the collection. Vectors are not type-safe either, and the collection iteration relies on methods in the collection class, rather than on a language construct like foreach. In addition, Java's distinction between Integer objects and int primitives results in some typecasting that is unnecessary in C#.

Empty catch clauses: In C#, the clause that follows the catch keyword and specifies which exception is caught is optional. If the catch clause is absent, the catch block is executed for any exception the try block throws. If the programmer doesn't care about the contents of the thrown exception, omitting the catch clause alleviates the burden of defining a variable (the thrown exception) that isn't used. I think the optional catch clause encourages programmers to be cavalier about ignoring error conditions, and therefore encourages poor programming practice. Compared to the absence of explicit exception declarations (the next item in this list), which I consider a serious design flaw, this minor language feature is a venial sin at worst.

No explicit exception declarations: In Java, a throws clause is mandatory for checked exceptions; in C#, throws doesn't exist. Novice Java programmers often complain that throws is tedious, and usually short-circuit their program exceptions by using try/catches with empty catch blocks. That technique is the software equivalent of putting pennies behind fuses: it works fine until something goes wrong. (Regular readers of my columns are not invited to point out that I use this technique myself in sample code for my articles. I don't do it in "real" programs.) By not requiring explicit exception declarations in method signatures, C# values short-term programmer convenience over program safety and correctness.

Interface and class definition syntax: C# replaces the Java extends and implements keywords with a colon. Java and C# are similar not only in syntax, but in architectural language design decisions. C#'s designers arrived at many of the same conclusions about how to "fix" C++ that Java's designers had reached 5 years (or so) earlier.

Both languages use automatic garbage collection for memory management (though C# allows access to pointer types and unmanaged memory in so-called "unsafe" code sections). Both have eliminated the delete operator. Neither currently has generic types, (implemented in C++ as templates), which are clearly needed. Both allow only single inheritance, offering interfaces as an arguably superior alternative to multiple inheritance. Both provide a hierarchical naming scheme, though C#'s namespaces are a more C++-like solution than are Java packages. Both have a base object type, from which all other classes are, by definition, derived. And so on. This does not necessarily mean that C# directly copies Java. Perhaps these decisions simply reflect the current consensus about what is desirable in this type of language.

The similarity between C++ and C# is a great benefit to any organization with an existing training investment in C and C++. Programmers accustomed to C++ will have no trouble understanding C#. Moreover, Windows programmers who invested time learning Java (often in the form of Visual J++) will come up to speed on C# even more quickly than C++ programmers. As I noted before, former Visual J++ programmers developing Windows applications will not have to choose between Java's ease of use and clean design and an API designed specifically for Windows. Writing to the Windows API sacrifices cross-platform compatibility in any case, and Microsoft would rather have a language it can control than one controlled by the Java Community Process. C# provides Windows developers with a language as easy to use as Java, and provides Microsoft with a language, targeted directly at its platform, that it can control entirely. Interestingly, C# has been submitted to a standards body, a topic I will discuss further in Part 2 of this article. If C# becomes standardized, it will be interesting to see how much control over C# Microsoft is really willing to relinquish.

C# and Java contrasts

C#'s most intriguing facets are its differences from Java, not its similarities. This section (and much of Part 2 of this series) covers features of C# that Java implements differently or entirely lacks.

Intermediate language

Microsoft is very flexible about choosing when MSIL is compiled to the native machine code. The company takes care to say that MSIL is not interpreted, but compiled to machine code. It also understands that many -- if not most -- programmers accept the idea that Java programs are inherently slower than anything written in C. The implication is that MSIL-based programs (written in C#, Visual Basic, "Managed C++" -- a version of C++ that conforms to the CLS -- and so on) will outperform "interpreted" Java byte code. Of course, this has yet to be demonstrated, since C# and other MSIL-producing compilers have not yet been released. But the ubiquity of JIT compilers for Java make Java and C# relatively equal in terms of performance. Statements like, "C# is compiled and Java is interpreted," are simply marketing spin. Java byte code and MSIL are both intermediate assembly-like languages that are compiled to machine code for execution, at runtime or otherwise.

COM integration

The biggest win for Windows programmers with C# may be its painless integration of COM, Microsoft's Win32 component technology. In fact, it will eventually be possible to write COM clients and servers in any .Net language. Classes written in C# can subclass an existing COM component; the resulting class can be used as a COM component too, and can then be subclassed in, for example, JScript, to provide yet a third COM component. The result is an environment in which components are network services, subclassable in any .Net language.

Microsoft's goal is to make component creation accessible from as many languages as possible, integrated within the .Net framework. Several vendors have already committed to creating .Net-enabled versions of programming languages as diverse as COBOL and Haskell. Developers could choose different languages to solve different problems. More important, programmers would not have to learn a new language to use .Net: they could choose one they already knew. For more on COM integration, see Jacques Surveyer's excellent survey of C# in Dr. Dobbs Journal. (See Resources.)

Best of breed or Franken-code?

The language interoperation goal certainly has its appeal. Imagine a system that uses "best of breed" languages for various tasks, "empowering" every developer to use his or her favorite language, "leveraging" existing information assets, and so on. The idea of being able to use any language anywhere, and even use multiple languages within a particular inheritance hierarchy, sounds exciting. However, I'm not sure I'm interested in that sort of excitement.

C# will initially ship with Visual Studio 7, which may be able to provide source-level debugging for multiple languages, an impressive achievement if Microsoft can make it work. But imagine actually working on a project of significant size in which multiple languages are used in a single application. Consider these concerns:

  • Would you want to manage that project? (Yeah, OK hotshot, you understand all those languages, because you're an über-hacker. Let me rephrase the question: Would you want your current boss to manage that project?)
  • How many languages are you using? Three? Six? Eight? What about that guy in your group who refuses to code in anything but APL, Haskell, or Prolog? There's a lot to like about those languages, but do you want to train everyone in your group to use them just so they can effectively debug the system? Or will Mr. Prolog have to sit in on all debugging sessions that use his code?
  • Let's say you're using six languages -- are you single-sourced for any of them? What will you do if the only company that makes the compiler for one of your languages goes out of business? Or if the other vendor's language products are incompatible with your code because your developers used language extensions proprietary to the original, now unsupported, language version?
  • What are the language version numbers for the n languages you're using to implement your system? If you think this question doesn't matter, think again. Languages may change more slowly than programs, but they do change.
  • Q. What's more of a headache than a bug in a compiler?

    A.

    Bugs in six compilers.

  • Microsoft's Common Language Subset, which describes language features necessary for .Net interoperation, places restrictions on languages that compile to MSIL. For example, Microsoft will provide Managed C++, which is C++ with some additional proprietary Microsoft "managed extensions." C++ programmers will have to learn to use these Microsoft-specific extensions. The same may be true for other languages.
  • What if you decide you want or need to change platform vendors? What if you someday decide that Microsoft's products aren't meeting your needs? Or some technology you desperately want or need doesn't integrate with .Net (possibly because Microsoft locks that technology out of .Net because it's a threat to other MS products)? Where will you go? And, more important, in this scenario, who decides where you go? (Hint: it's not you.)

With a little thought, I'm sure you can add to this list. The reality probably isn't quite that bad, though. Components written in multiple languages comprise many large data processing systems (the World Wide Web, for example). Most servers, and many applications, are extensible in multiple scripting and/or compiled languages. Used wisely, language mix-and-match in a project can provide needed flexibility and power. But only if the languages are selected for valid architectural reasons, not simply to "leverage" (salvage) creaky, poorly structured legacy code, to make use of undertrained entry-level programmers, or to satisfy managers cutting corners on training costs.

Conclusion

This article, the first in a two-part series, has provided a superficial overview of C#, focusing on its similarity to Java. The next article will cover C#'s language features, and demonstrate that C# and Java are actually quite different; they have many subtle semantic differences and design choices, and fill different technological and market niches. I'll also cover Microsoft's attempts to standardize C#, and what it may mean to Java. Stay tuned next month.

Mark Johnson is a writer, a consultant, and a JavaBeans columnist and XML guru at JavaWorld. His interests include design patterns, structured data representation, software architecture, enterprise data systems, and codeless software development. He is president of elucify technical communications (etc), a Colorado-based corporation dedicated to clarifying novel or complex ideas through clear explanation and example. His client list currently includes Sun Microsystems, where he is a contract technical writer.
1 2 3 Page 2
Page 2 of 3