next up previous contents
Next: MPI with C# Up: Comparison of Languages Previous: C++   Contents

C#

C# [20], which is an important part of the new Microsoft .NET platform, is a modern, object-oriented and type-safe programming language based on C and C++. Header files, Interface Definition Language, and complicated interfaces are not needed in C#. C# is supposed to offer an alternative to C++ programmers who find that language too complicated, and to Java programmers who miss certain features of C and C++, like pointers, operator overloading, and templates. C# has all the data types provided by the Java, with additional unsigned counterparts and a new 12-byte-decimal floating-point number. Java uses primitive types that are distinguished from object-based types. Java primitive types must be put into an instance of a wrapper class to participate in the object-based world. C# provides what it calls a ``unified type system''. This means all types--including value types--derive from the type object. Primitive types are stack-allocated as in Java, but are also considered to derived from the ultimate base class, object. This means that the primitive types can have member functions called on them. The example:

$\displaystyle \begin{minipage}[t]{\linewidth}\small\verb$ using System;$\\
\v...
...nsole.WriteLine(3.ToString());$\\
\verb$ }$\\
\verb$ }$\\ 
\end{minipage}
$

calls the object-defined ToString method on an integer literal. Whenever a primitive type is used in a situation where a parameter of type object is required, the compiler will automatically box the primitive type into a heap-allocated wrapper. An ``object box'' is allocated to hold the value, and the value is copied into the box. Unboxing is just the opposite. When an object box is cast back to its original value type, the value is copied out of the box and into the appropriate storage location. For example:

$\displaystyle \begin{minipage}[t]{\linewidth}\small\verb$ class Test$\\
\verb...
...$ int j = (int) o; // unboxing$\\
\verb$ }$\\
\verb$ }$\\ 
\end{minipage}
$

Declaring the object variable is done for illustration only; in real code, i would be passed directly, and the boxing would happen at the call site. Unlike Java we can pass method arguments by reference in C# using ref and out modifiers. A reference parameter is declared with a ref modifier. The same storage location is used between a reference parameter and the variable given as the argument. An example usage of these parameters may look like:

$\displaystyle \begin{minipage}[t]{\linewidth}\small\verb$ void Swap(ref int x, ...
...erb$ x = y;$\\
\verb$ y = temp;$\\
\verb$ }$\\ 
\end{minipage}\verb$ $\\
$

A out modifier is used to declare a output parameter. Like reference parameters, output parameters do not create a new storage location. An output parameter need not be assigned before it is passed as a argument of a method. C# also provides C-like pointers through unsafe code. In order to use unsafe code we should specify the unsafe modifier on the code block. Unsafe code can be used in place of Java Native Methods in many cases. As mentioned in section 2.4.1, the Java Grande Numerics Working Group identified various critical areas to improve the Java language. C# addresses some of issues by supporting structs, operator overloading, and multidimensional arrays. Structs can be used instead of classes when the user wants to create an object that behaves like one of the built-in types; one that is cheap and fast to allocate and doesn't have the overhead of references. Structs act similarly to classes, but with a few added restrictions. They are value types rather than reference types, and inheritance is not supported for structs. Struct values are stored either ``on the stack'' or ``in-line''. C# provides operator overloading that allows user-defined operators to be implemented on classes or structs so that they can be used with operator syntax. Unlike C++, it is not possible to overload member access, member invocation (function calling), or the +, &&, $ \parallel$, ?:, or new operators. The new operation can't be overloaded because the .NET Runtime is responsible for managing memory. Like our HPJava system, C# supports both ``rectangular'' and ``jagged'' multi-dimensional arrays[*]. Rectangular arrays always have a rectangular shape. Given the length of each dimension of the array, its rectangular shape is clear. A jagged array is merely an array of arrays and it doesn't have to be square. Microsoft's .NET framework is based on its Common Language Runtime (CLR), which is a specification for language-independent intermediate language (IL) code, and a runtime that provides memory management and security. Whereas Java programs can run on any platform supporting JVM, and are compiled to byte code, which is an intermediate language only for Java, C# demands the standardization of Microsoft intermediate language (MSIL) [1]. Unlike Java, which is platform independent, MSIL has a feature called language independence: code and objects written in one language can be compiled to MSIL format and interoperate with other languages. However, for classes to be usable from .NET languages in general, the classes must adhere to the Common Language Specification (CLS), which describes what features can be used internally in a class. This significantly restricts C++ programs, for example, if they are to run under .NET. The MSIL instruction set is very similar in many ways to the JVM instruction set. The MSIL assumes a stack-based abstract machine very similar to the JVM, with a heap, a frame stack, the same concept of stack frame, and bytecode verification. There are some rather simple differences--for example JVM words are big endian (most significant byte first) whereas MSIL uses a little endian (least significant byte first) binary representation. Also MSIL instructions do not include information that specifies the type of the arguments. Rather, that is inferred by what is been pushed on the stack. We can find more fundamental differences between JVM instruction sets and MSIL in the verification of compiled code, and pointers. JVM verification ensures that the binary representation of a class or interface is structurally correct. The following lists of checks are performed during JVM verification. Unlike JVM, several important uses of MSIL instructions are not verifiable, such as the pointer arithmetic versions of add that are required for the faithful and efficient compilation of C programs. For nonverifiable code, memory safety is the responsibility of the application programmer. The only kind of pointer manipulated by the JVM instruction set is the object reference. MSIL manipulates three different kinds of pointers: object reference like JVM, managed pointers for reference argument variables, and unmanaged pointers for unsafe code pointers. Managed pointers must be reported to the garbage collector. The garbage collector can modify the managed pointer itself, as well as the contents of the location which is pointed to. The unmanaged pointers get great flexibility in their operation since they are not reported to the garbage collector. But memory safety is endangered by performing arithmetic on unmanaged pointers; hence they are not verifiable.

Subsections
next up previous contents
Next: MPI with C# Up: Comparison of Languages Previous: C++   Contents
Bryan Carpenter 2004-06-09