ONDotNet.com    
 Published on ONDotNet.com (http://www.ondotnet.com/)
 See this if you're having trouble printing code examples


Contrasting C# and Java Syntax

by Raffi Krikorian
06/14/2001

In the first article of this series, I translated a trivial Java program into C# and pointed out the differences between the two implementations. This time through, we are going to focus specifically on the syntax of C#. What I am going to do is point out when the syntactical structures are the same, and spend time talking about when they are different. There are a few syntactical "extras" in C# that do not have equivalents in Java, and I am going to spend time pointing those out as we go along. Let us start small and then go big.

Primitive and Simple Types

Java has a couple of primitive types that everybody is familiar with: byte, char, int, long, float, double. Primitives are the basic building blocks of Java; they are the smallest "unit." What usually annoys annoys most programmers is that primitives are disjoint from the object model of Java; whereas all objects in Java extend from java.lang.Object, primitives do not extend from anything. This means that any class that operates on objects (i.e., objects in the Collections API) will not work with primitives. The primitives must be mapped into the object model in order to use it.

This is not the case in C#. C# uses the .NET object/type system so that C# programs may inter-communicate with other .NET languages without having type confusion -- for example the, type int is an alias to System.Int32 which in turn ultimately extends from System.Object. This means that the primitive, or simple, types in C# function just like any other object. For example, it is valid to call the C# ToString or GetType method on any simple type.

Table 1: Primitive Types in Java and C#
A mapping from the familiar Java primitive types to their C# simple type equivalents (and the objects they actually alias from).
Java C#
boolean bool (System.Boolean)
byte sbyte (System.Sbyte)
char char (System.Char)
int int (System.Int32)
long long (System.Int64)
float float (System.Single)
double double (System.Double)

Even though the C# simple types are technically objects, they are still passed by value just like their Java counterparts. (Java primitives are passed by value and Java objects are passed by reference.) This is the case because aside from being objects, the C# simple types are all structs -- objects that are passed by value as opposed to being passed to reference (we will get to these further on).

Related articles:

Conversational C# for Java Programmers

Comparing C# and Java

Comment on this articleWhat are your thoughts on the learning curve involved with learning C# syntax as a Java or C++ programmer?
Post your comments

Statements

Now that we have primitive data structures, we should start organizing them into higher level statements. You will note that there is not much to say to Java programmers as they are already familiar with most of these syntaxes; we will focus on the differences between Java and C#.

Declarations

Variables are defined in C# just like they are in Java:

int integer = 3;
bool boolean = true;

Java uses the "static final" keywords to make a variable constant; in Java a "static final" variable is class-scoped instead of being object-scoped, and the compiler prevents any other object from changing the value of the variable. C#, in turn, has two ways of declaring a variable constant.

Marking a variable const causes the value to be inlined before compiling. With the definition

const int TWO = 2;

the expression

2 * TWO

is converted to

2 * 2

by the preprocessor before compilation. This causes the compiled program to run faster as it does not have to look up the value of the constant during run-time.

Considering that constants are often used for things like BUFFERSIZE or TIMEOUT, it may be desirable not to have this inlined into the code; if this field is marked const, then any code compiled against it will inline the constant and will need to be recompiled in order for it to change. If the constant is instead marked with readonly, then the next time the application is executed the behavior will change as the code will check the value of the readonly field, while the compiler still protects it from being programmatically changed.

Conditionals

The two conditional structures that Java programmers expect, the "if-then-else" and "switch" statements, are both available in C#. Both behave similarly except for one subtle difference in the C# "switch" statement syntax.

Java allows for control flow to implicitly fall between different cases in the switch statement, whereas the C# compiler explicitly does not allow this -- the C# compiler will mark this as a syntactical error. For example, in Java the following is legal:

int switching = 3;
switch( switching ) {
case 3:
  System.out.println( "here" );
default:              
  System.out.println( "and here" );

}

This will result in both printlns being executed. In C#, an explicit break or goto case to a different case is required somewhere in the case to explicitly define the control flow.

Loops

C# has the familiar "while," "do-while," and "for" loops. On top of these three, C# has introduced a "foreach" loop that operates on objects that implement the System.Collections.IEnumerable interface. Most notably, arrays in C# (System.Array) implements IEnumerable, so the following Java code

int[] array = // something...
for( int count=0;count<array.length;count++ ) 
  System.out.println( array[count] );

is legal in C#, but it can also be represented in C# as

foreach( int member in array )
  Console.WriteLine( member );

Specifically, the IEnumerable interface provides the ability to get an IEnumerator representation to an object (similar to java.util.Enumeration). Anything which implements the IEnumerable and IEnumerator interfaces can be operated on by the "foreach" loop.

Jumps

Most of the Java jump statements map into C#: break, continue, goto, and return. These statements are used the same way they are used in Java: to break out of loops or to return control to a different block.

What deserves a discussion is the exception model, as it differs both semantically and syntactically. As mentioned in my previous article, all exceptions in C# are run-time exceptions; the compiler is not going to help the programmer keep track of exceptional situations. It should also be noted that method signatures do not contain the exception that can be thrown inside the block. My warning to Java programmers turned C# programmers: be very careful.

The "try-catch-finally" block that Java programmers are familiar with exists in C#, and so do a few additional syntactic sugars. All exceptions derive from System.Exception, so catching System.Exception will catch all possible exceptions thrown in a block, just like in Java. A shortcut is available if the exception object is not needed by using the following framework:

try {
  // something that may thrown an exception   

} catch {
  // deal with the exception without getting a handle
  // to the exception object

}

But if you do need to catch a specific exception (while again not requiring the exception object), try something like

try {
   
} catch( IOException ) {

}
Table 2: Useful properties of Exceptions in Java and C# Exception messages and exception stack traces are usually needed by programmers and logs
Java C#
getMessage() Message
printStackTrace() StackTrace (this is not exactly equivalent as printStackTrace and its Java related method prints the stack trace to a stream, whereas StackTrace returns a C# string)

Methods

Methods act just as we would expect them to behave. At the basic level there are no differences between C# and Java methods; each method takes parameters, and has a return type.

class A {
  public void MethodA1() { }

  public int MethodA2() { }
  public void MethodA3( int i ) { }
  public int MethodA4{ int i ) { }

}

As we believe it should, this creates a class A that has one method named MethodA1 that does not take any parameters and does not return any value. MethodA2 returns an integer, MethodA3 takes an integer as a parameter, and MethodA3 both returns an integer and takes an integer as a parameter.

But also as we expect, C# has a few more things that we can do with methods that we could not do before in Java.

params

When calling a method, the Java compiler checks that there is a method that matches its signature. For example, in Java, if there is a method defined as

public void methodCaller( int a, int b, int c );

then a call of that method

methodCaller( 3, 4, 5, 6 );

will generate a compiler error as the Java compiler cannot find a method named methodCaller that takes four ints. We cannot create a method signature that allows us to take a variable number of ints as parameters.

Why should this even matter? If a programmer wants to take a variable number of ints, he can define a method signature that takes an array of integers and then construct the array when calling the method. In Java, this will take at least three lines of code just to call the method -- the first to create the array, one or more lines to assign values into the array, and the third to call the method. C# attacks this issue by allowing programmers to place a params modifer on the last array parameter of the method, so that a variable number of parameters can be passed into it. Using the above example, the method signature can be defined as

public void methodCaller( params int[] a );

and the method can be called with any of

methodCaller( 1 );
methodCaller( 1, 2, 3, 4, 5 );

Inside methodCaller, the parameters can be accessed through the "a" array defined.

ref and out

C# can also force a parameter to be passed in by reference as opposed to being passed by value. Taking a primitive type, for example

public void increment( int a ) {
  a++;

}

int a = 3;
increment( a );
// a still == 3

will not cause the variable passed into increment to actually be incremented because a is being passed in by value; the variable a in the increment block is in a different environment, and therefore changes in that scope do not propagate back out.

However, changing the method to

public void increment( ref int a ) {

  a++;

}

int a = 3;
increment( ref a );
// a now == 4

will cause the int a to be passed in by reference (just as classes are passed in) and therefore modifying it will cause the original value to be changed. Using ref with a class allows a function to reassign the class pointer inside the function and have that change propagate outside the method.

A natural partner to ref is the out keyword. While ref does not guarantee that any operations will actually be performed on the variable, using out will cause the compiler to make sure that a value has been set to the variable. Code such as

public void DoNothing( out int a ) { }

will cause the compiler to complain as a is not assigned to anything.

Properties

Properties are a C# construct that formalizes the getter/setter pattern seen in many Java classes. The Java paradigm of having a get method that takes one parameter and a set method that returns the said parameter is compacted. So the following Java code:

private int property;
public int getProperty() {
  return this.property;

}

public void setProperty( int property ) {
  this.property = property;

}

can be written in C# as

private int property;
public int Property {
  get {
    return this.property;

  }

  set {
    // value is an implicit variable generated by the
    //  compiler to represent the parameter
    this.property = value;

  }

}

which can be easily used from inside of a C# program with just

int currentValue = Property;
Property = newValue;

Behind the scenes, C# has actually compiled the property to two methods in the .NET intermediate language framework named get_Property and set_Property. These methods cannot be called directly from C#, but other languages using the MSIL should be able to access these getters/setters.

Accessibility Modifiers

Access modifiers do just what their names imply -- they restrict the ability for only certain code to change a field. The ones that we are used to are private, protected, default, and public. C#, in turn, has five modifiers

These modifiers can be applied to the same structures that Java allows you to use them with: you can modify the accessibility to objects, methods, and variables. We are mentioning these here, so we can talk about objects and inheritance next.

Objects, Classes, and Structs

All Java programmers have to already be familiar with the concepts of classes, objects, and inheritance, so learning the C# equivalent is just a manner of showing the syntactical differences. Defining a class is trivial:

class A {

}

will do it for you. And extending another uses the : instead of the extends keyword so

class B : A {

}

means that B extends A.

Interfaces can be implemented in the same (albeit slightly confusing to the Java programmer) manner with interface C being implemented by class D with

class D : C { 

}

and D can both extend B and implement C in

class D : B, C {

}

Note that B has to be placed before C in the list, and any other interfaces to implement are added to the comma-separated list.

All classes are passed by reference to method calls. This means that the variable being defined and passed around is actually a reference to another memory space containing the actual object. Everything in Java, except the primitives, is passed by reference -- there is no way to define anything that can be passed by value. To define an object that is passed by value in C#, a syntactical construct named a "struct" can be used.

struct E {

}

The struct looks and acts exactly as a class (they even all derive from System.Object), except with the following restrictions:

Using a struct for a custom type does have an advantage in that it can be very efficient to allocate. When allocating arrays of classes, you first have to define an array of references. Then the program needs to iterate through this array to instantiate each class one by one. Merely allocating an array of structs will allocate each individual struct in a contiguous block. As mentioned above, each simple type in C# is defined as a struct which provides it the standard pass-by-value semantics that Java programmers are used to.

Self Referentiation

Self referentiation is possible within C# objects just as they are within Java objects. this means semantically the same thing, but C# uses the keyword base instead of super. Both this and base can be used in methods and constructors just as this and super can be used in Java.

Overriding Methods

All methods in a object are "final" (in the Java sense of the word) by default. In order for a method to be overridden by an inheriting object, the original method needs to be marked "virtual" and all methods overriding the virtual method in the inherited classes must be marked "override." For example:

public class A : Object {
  public virtual void toOverride() { }

}

public class B : A {
  public override void toOverride() { }

}

is required in C# for the compiler not to complain when defining the inheriting class.

Type Conversion

Java programmers are usually only familiar with type conversion between primitive types, and when upcasting or downcasting an object to its superclass or subclass. C# allows for the ability to define custom type conversions between any two objects. Two types of transformations should be kept in mind:

In the following example, we provide the ability to convert a double to a FlooredDouble, and vice versa. We need to define an explicit conversion for double to FloorDouble, as information may be lost in the conversion. For the other way around, an implicit conversion may be defined, as no information is going to be lost.

public class FloorDouble : Object {
  private double value;

  public FloorDouble( double value ) {
    this.value = Math.Floor( value );

  }

  public double Value {
    get {
      return this.value;

    }

  }

  public static explicit operator FloorDouble( double value ) {
    return new FloorDouble( value );

  }

  public static implicit operator double( FloorDouble value ) {
    return value.Value;

  }

}

// this class can be used by
FloorDouble fl = (FloorDouble)10.5
double d = fl;

Operator Overloading

Wouldn't it be nice if Java programs could take two java.lang.Double objects and just use + to add them together and get another Double object back? Operator overloading in C# is simple. The FloorDouble class defined above can be extended to contain the static method

public static FloorDouble operator + ( FloorDouble fd1, FloorDouble fd2 ) {
  return new FloorDouble( fd1.Value + fd2.Value );

}

and then statements such as

FloorDouble fd1 = new FloorDouble( 3.5 );
FloorDouble fd2 = new FloorDouble( 4 );
FloorDouble sum = fd1 + fd2;

become legal.

Organizing Source Code

C# does not place any requirement on file organization -- a programmer could clump the entire C# program into one .cs file. (Java, on the other hand, usually requires one .java file per Java class.)

The C# language does, however, provide a way to conceptually separate out the program's objects in a manner similar to Java's packages. Using namespaces, related types can be grouped into a hierarchy. With no "namespace" block, the program exists in a default namespace. To define one, use

namespace com.oreilly {

}

and all types defined in the block exist in the com.oreilly hierarchy. These hierarchies can also be nested with

namespace com {
  namespace oreilly {

  }

}

allowing code to be written in both enclosing namespaces.

To import a namespace, the "using" keyword is needed. This functions basically like the "import" statement in Java, but lacks the ability to import a specific class; only entire namespaces can be imported. using can also be used to alias types and namespaces and save you valuable keystrokes. For example a class defined like

namespace a.really.long.namespace {
  class theClass {

  }

}

can be aliased to something a lot shorter with using, as in

using short = a.really.long.namespace.theClass;

Conclusion

Related articles:

Conversational C# for Java Programmers

Comparing C# and Java

Note that this article is nowhere near complete in covering all the syntaxes in C#; we did not cover enums, delegates, unsafe code, the preprocessor, and many other statement types. We instead covered a list of statements which should be familiar and pertinent to those familiar with Java. And knowing what you already know about Java programming, you should be able to use the statements listed above and my previous article to get you on your feet and to create a full-fledged C# program.

Raffi Krikorian makes a career of hacking everything and anything. Professionally, he is the founding partner at Synthesis Studios: a technological design and consulting firm that orchestrates his disjointed train of thought.


Return to the .NET DevCenter.

Copyright © 2009 O'Reilly Media, Inc.