Next: MPI with C#
Up: Comparison of Languages
Previous: C++
Contents
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:
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:
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:
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 +,
&&,
, ?:, 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.
- Every instruction has a valid operation code.
- Every branch instruction branches to the start of some other
instruction, rather than into the middle of an instruction.
- Every method is provided with a structurally correct signature.
- Every instruction obeys the type discipline of the Java language.
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: MPI with C#
Up: Comparison of Languages
Previous: C++
Contents
Bryan Carpenter
2004-06-09