The C# Programming Language for Java Developers

The C# Programming Language for Java Developers

This document discusses some of the similarities and differences between Java and C#, in order to get a grasp of what's involved when migrating to .NET. The key similarities between Java and C# are:

  • Both Java and C# have their roots in C++ and share features with that language.
  • Both languages compile to an intermediate code rather than pure machine code. Java compiles to what is known as Java Virtual Machine (JVM) bytecode, while C# compiles to Common Intermediate Language, CIL.
  • Java bytecode is executed by an application called the Java virtual machine (JVM). Similarly, compiled C# programs are executed by the Common Language Runtime, or CLR.
  • Exception handling in C# is very similar to Java, with some minor differences. C# handles run-time errors (also called exceptions) with the try...catch construct, just as in Java. The System.Exception class is the base class for all C# exception classes.
  • Like Java, C# is strongly type checked. The compiler detects where types are used in a way that may cause errors at run time.
  • Like Java, C# provides automatic garbage collection, freeing the programmer from keeping track of allocated resources.
  • Both Java and C# support single inheritance and multiple interface implementations.

Let's now look at the important differences that we'll cover in this document:

  • The C# language introduces new language constructs like foreach, indexers, attributes, delegates, operator overloading and other language constructs. These are discussed in more detail later in the document.

Contents

Source File Conventions
    Top-level Declarations
    Fully Qualified Names and Namespace Aliases
    Pre-Processing Directives
Language Syntax
    Converting and Casting
    Value and Reference Types
    Boxing and Unboxing
Operators
Flow Control
Class Fundamentals
    Access Modifiers
The Main() Method
Passing by Reference
Using an Indeterminate Number of Parameters
Properties
Structs
Arrays in C#
Inheritance and Derived Classes
Method Overriding
Advanced C# Techniques
Garbage Collection
Summary
 

Source File Conventions

There are some differences in the file naming conventions and structure of source programs in the two languages that we need to be aware of.

File Naming

The naming convention for files containing C# classes is a little different from Java. Firstly, in Java, all source files have a .java extension. Each source file contains one top-level public class declaration, and the class name must match the filename. In other words, a class called Customer declared with public scope must be defined in a source file with the nameCustomer.java

C# source code on the other hand is denoted by the .cs extension. Unlike Java, source files can contain more than one top-level public class declaration, and the filename doesn't need to match any of the classes' names.

Top-level Declarations

In both Java and C#, source code begins with a few top-level declarations in a certain sequence. There are only a few differences in the declarations made in Java and C# programs.

Top-level Declarations in Java

In Java, we can group classes together with the package keyword. A packaged class must use the package keyword in the first executable line of the source file. Any import statements required to access classes in other packages appear next, and then comes the class declaration, like so:

package <Package name>;
import <package hierarchy>.<class name>;
class Customer
{
...
}

Top-level Declarations in C#

C# uses the concept of namespaces to group logically related classes through thenamespace keyword. These act similarly to Java packages, and a class with the same name may appear within two different namespaces. To access classes defined in a namespace external to the current one, we use the using keyword followed by the namespace name, as shown below:

using <namespace hierarchy>.<class name>;
namespace <namespace name>
{
  class Customer
  {
    ...
  }
}

Note that using statements may quite legally be placed inside a namespace declaration, in which case such imported namespaces form part of the containing namespace.

Java does not allow multiple packages in the same source file, while C# does allow multiple namespaces in a single .cs file:

namespace AcmeAccounting
{
  public class GetDetails
  {
    ...
  }
}

namespace AcmeFinance
{
  public class ShowDetails
  {
    ...
  }
   
}

Fully Qualified Names and Namespace Aliases

Like Java, we can access classes in both .NET or user defined namespace without ausing reference for that namespace, by providing the fully qualified name for the class, such asSystem.Data.DataSet or AcmeAccounting.GetDetails in the above example.

Fully qualified names can get long and unwieldy, and in such cases, we can use theusing keyword to specify a short name, or alias, to make our code more readable.

In the following code, an alias is created to refer to code written by a fictional company:

using DataTier = Acme.SQLCode.Client;
using System;

public class OutputSales
{
  public static void Main()
  {
    int sales = DataTier.GetSales("January");
    Console.WriteLine("January's Sales: {0}", sales);
  }
}

Note the syntax forWriteLine(), with {x} in the format string, where x denotes the position in the argument list of the value to insert at that position. Assuming the GetSales() method returned 500, the output of the application would be:

January's Sales: 500

Pre-Processing Directives

Similar to C and C++, C# includes pre-processor directives that provide the ability to conditionally skip sections of source files, report error and warning conditions, and to delineate distinct regions of source code. The term "pre-processing directives" is used only for consistency with the C and C++ programming languages as C# does not include a separate pre-processing step. For a full list of C# Pre-processor directives, see C# Pre-processor directives.

Language Syntax

In this section, we discuss the similarities and differences between the two languages. Some of the major differences are:

  • Declaration of constants – Java uses thefinal keyword for this, while C# uses the keywordsconst or readonly
  • Compound data types – in Java, we can create compound data types as classes without methods using the class keyword, but C# offers struct for this purpose, as in C.
  • Destructors – C# allows us to create a destructor method that is called before instances of a class are destroyed. In Java, a finalize() method can be provided to contain code that cleans up resources before the object is garbage-collected. In C#, this function is provided by the class destructor. The destructor resembles a constructor with no arguments and a preceding tilde character ~.
  • Function pointers – C# provides a construct called delegate to create a type-safe function pointer. Java does not have any such equivalent.
Data Types

C# provides all the data types that are available in Java, and adds support for unsigned numerals and a new 128-bit high precision floating-point type.

For each primitive data type in Java, the core class library provides a wrapper class that represents it as a Java object. For example, theInteger class wraps the int data type, and the Double class wraps the doubledata type.

On the other hand, all primitive data types in C# are objects in the Systemnamespace. For each data type, a short name, or alias, is provided. For instance, int is the short name forSystem.Int32 and double is the short form of System.Double

The list of C# data types and their aliases is given below. As you will notice, the first 8 of these correspond to the primitive types available in Java. Note however that Java's boolean is calledbool in C#.

C# Data Types
Short Name.NET ClassTypeWidthRange (bits)
byteSystem.ByteUnsigned integer80 to 255
sbyteSystem.SByteSigned integer8-128 to 127
intSystem.Int32Signed integer32

 -2,147,483,648 to

2,147,483,647

uintSystem.UInt32Unsigned integer320 to 4294967295
shortSystem.Int16Signed integer16-32,768 to 32,767
ushortSystem.UInt16Unsigned integer160 to 65535
longSystem.Int64Signed integer64

 -922337203685477508 to

922337203685477507

ulongSystem.UInt64Unsigned integer640 to 18446744073709551615
floatSystem.SingleSingle-precision floating point type32

 -3.402823e38 to

3.402823e38

doubleSystem.DoubleDouble-precision floating point type64

 -1.79769313486232e308 to

1.79769313486232e308

charSystem.CharA single Unicode character16Unicode symbols used in text
boolSystem.BooleanLogical Boolean type8True or false
objectSystem.ObjectBase type of all other types  
stringSystem.StringA sequence of characters  
decimalSystem.DecimalPrecise fractional or integral type that can represent decimal numbers with 29 significant digits128-2 x 10-96 to 2 x 1096


Because C# represents all primitive data types as objects, it is possible to call an object method on a primitive data type. For example:

int i=10;
object o=i;
Console.WriteLine(o.ToString());

This is achieved with the help of automatic boxing andunboxing. For more information, see Boxing and Unboxing.

Enums

Similar to C/C++, and not available in Java, enums or enumerations are used to group named constants. The example below defines a simple Color enumeration.

public enum Color {Green, Orange, Red, Blue}

Integral values can also be assigned to enums as shown in the following enum declaration:

public enum Color {Green=10, Orange=20, Red=30, Blue=40}

The program below calls theGetNames method of the Enum type to display the available constants for an enumeration. It then assigns a value to an enum and displays the value.

using System;

public class TypeTest
{
    public static void Main()
    {
      Console.WriteLine("Possible color choices: ");
      //Enum.GetNames returns a string array of named constants for the enum
      foreach(string s in Enum.GetNames(typeof(Color)))
      {
        Console.WriteLine(s);
      }
      Color FavoriteColor = Color.Blue;
      Console.WriteLine("Favorite Color is {0}",FavoriteColor);
      Console.WriteLine("Favorite Color value is {0}", (int)FavoriteColor);
    }
}

After running, the program below would display the following output:

Possible color choices:
Green
Orange
Red
Blue
Favorite Color is Blue
Favorite Color value is 40

Strings

String types in both Java and C# exhibit similar behavior with slight differences. Both string types are immutable, meaning that the values of the strings cannot be changed once the strings have been created. In both instances methods that appear to modify the actual content of a string actually create a new string to return, leaving the original string unchanged. The process of comparing string values is different in C# and Java. To compare string values in Java, developers would need to call the equals() method on a string type as the == operator compares reference types by default. In C#, developers can use the == or != operators to compare string values directly. Even though a string is a reference type in C#, the == and != operator will, by default, compare the string values rather then references. We'll discuss value types and references later in this document.

Just like in Java, C# developers should not use the string type for concatenating strings to avoid the overhead of creating new string classes every time the string is concatenated. Instead, developers can use theStringBuilder class in the System.Textnamespace which is functionally equivalent to the JavaStringBuffer class.

String Literals

C# provides the ability to avoid the usage of escape sequences like "/t" for tab or "/" for backslash characters within string constants. To do this, simply declare the verbatim string using the @ symbol to precede the assignment of the string value. The examples below show how to use escape characters and how to assign string literals:

//Using escaped characters
string path = "FileShare//Directory//file.txt"

//Using String Literals
string escapedPath = @"//FileShare/Directory/file.txt"

Converting and Casting

Both Java and C# follow similar rules for automatic conversions and casting of data types.

Like Java, C# supports bothimplicit and explicit type conversions. In the case of widening conversions, the conversions are implicit. For example, the following conversion fromint to long is implicit, as in Java:

int intVariable = 5;
long l = intVariable;

Below is a list of implicit conversions between .NET data types:

Implicit Conversions
Source TypeTarget Type
byteshort, ushort, int, uint, long, ulong, float, double, or decimal
sbyteshort, int, long, float, double, or decimal
intlong, float, double, or decimal
uintlong, ulong, float, double, or decimal
shortint, long, float, double, or decimal
ushortint, uint, long, ulong, float, double, or decimal
longfloat, double, or decimal
ulongfloat, double, or decimal
floatdouble
charushort, int, uint, long, ulong, float, double, or decimal


We cast expressions that we wish to explicitly convert using the same syntax as Java:

long longVariable = 5483;
int intVariable = (int)longVariable;

Explicit Conversions
Source TypeTarget Type
bytesbyte or char
sbytebyte, ushort, uint, ulong, or char
intsbyte, byte, short, ushort, uint, ulong, or char
uintsbyte, byte, short, ushort, int, or char
shortsbyte, byte, ushort, uint, ulong, or char
ushortsbyte, byte, short, or char
longsbyte, byte, short, ushort, int, uint, ulong, or char
ulongsbyte, byte, short, ushort, int, uint, long, or char
floatsbyte, byte, short, ushort, int, uint, long, ulong, char, ordecimal
doublesbyte, byte, short, ushort, int, uint, long, ulong, char, float, or decimal
charsbyte, byte, or short
decimalsbyte, byte, short, ushort, int, uint, long, ulong, char, float, or double


Value and Reference Types

C# supports two kinds of variable types:

  • value types – these are the built-in primitive data types, such as char, int,float, and user defined types declared withstruct
  • reference types – classes and other complex data types that are constructed from the primitive types. Variables of such types do not contain an instance of the type, but merely a reference to an instance.

Let's explore this a little further. If we create two value type variables, i andj, like so:

int i = 10;
int j = 20;

memory locations

Figure 1: Memory locations for value types

then i andj are completely independent of each other; they are given separate memory locations:

If we change the value of one of these variables, the other will naturally not be affected. For instance, if we have an expression such as this:

int k = i;

then there is still no connection between the variables. That is, if we then change the value of i and k will remain at the value that i had at the time of the assignment.

Reference types however act differently. For instance, we could declare two variables like so:

myClass a = new myClass();
myClass b = a;

Now because classes are reference types in C#, a is known as a reference tomyClass The first of the above two lines creates an instance of myClass in memory, and sets ato reference it. Thus, when we set b to equal a, it contains a duplicate of the reference to the class in memory. If we now change properties on b, properties on a would reflect these changes, because both point to the same object in memory, as shown in this figure:

memory locations

 

Figure 2: Memory locations for reference types

Boxing and Unboxing

The process of converting a value type to a reference type is called boxing. The inverse process, converting a reference type to a value type, is called unboxing. This is illustrated in the following code:

int valueVariable = 10;

// boxing
object obj = refVariable;

// unboxing
int valueVariable = (int) refVariable;

Java requires us to perform such conversions manually. Primitive data types may be converted into objects of wrapper classes by constructing such objects (boxing). Similarly, the values of primitive data types may be extracted from the objects of wrapper classes by calling an appropriate method on such objects (unboxing). For more information on boxing, see Boxing Conversion, for more information on unboxing see Unboxing Conversion.

Operators

C# offers all applicable operators supported by Java, as listed in the following table. At the end of the table, you'll see some new operators available in C# but not Java:

Operators
CategorySymbol
Unary++ -- + - ! ~ ()
Multiplicative* / %
Additive+ -
Shift<< >>
Relational< > <= >= instanceof
Equality== !=
Logical AND&
Logical XOR^
Logical OR|
Conditional AND&&
Conditional OR||
Conditional? :
Assignment= *= /= %= += -= <<= >>= &= ^= |=
Type of Operandtypeof
Size of Operandsizeof
Enforce Overflow Checkingchecked
Suppress Overflow Checkingunchecked


The only Java operator not available in C# is the >>> shift operator. This operator is present in Java as a consequence of the lack of unsigned variables in that language, for cases when right- shifting is required to insert 1s in the most significant bits.

C# however does support unsigned variables, and thus C# only needs the standard>> operator. This operator produces different results depending on whether the operand is signed or unsigned. Right-shifting an unsigned number inserts 0 in the most significant bit while right-shifting a signed number copies the previous most significant bit.

The checked and unchecked Operators

Arithmetic operations will result in overflow if the result is too large for the number of bits allocated to the data type in use. Such overflow can be checked or ignored for a given integral arithmetic operation using the checked and unchecked keywords. If the expression is a constant expression using checked, an error would be generated at compile time.

Here's a simple example to illustrate these operators:

using System;
public class Class1
{
  public static void Main(string[] args)
  {
    short a = 10000, b = 10000;
    short d = unchecked((short)(10000*10000));
    Console.WriteLine(d= + d);

    short c = (short)(a*b);
    Console.WriteLine(c= + c);

    short e = checked((short)(a*b));
    Console.WriteLine(e= + e);
  }
}

In this code, the unchecked operator circumvents the compile time error that would otherwise be caused by the following statement:

short d = unchecked((short)(10000*10000));

The next expression is unchecked by default, so the value will silently overflow:

short c = (short)(a*b);

We can force the expression to be checked for overflow at run time with the checked operator:

short e = checked((short)(a*b));

When run, assigning the first to values to d & c will silently overflow with a value of -7936, but when attempting to multiply the value for e using checked(),the program will throw a System.OverflowException

Note: You can also control whether to check for arithmetic overflow in a block of code by using the command line compiler switch (/checked) or directly in Visual Studio on a per project basis.

The is Operator

This operator determines whether the type of the object on the left hand side matches the type specified on the right:

if (objReference is SomeClass) ...

In the following example, the CheckType() method prints a message describing the type of the argument passed to it:

using System;

public class ShowTypes
{

  public static void Main(string[] args)
  {
    CheckType (5);
    CheckType (10f);
    CheckType ("Hello");
  }

  private static void CheckType (object obj)
  {
    if (obj is int)
      {
        Console.WriteLine("Integer parameter");
      }
      else if (obj is float)
      {
        Console.WriteLine("Float parameter");
      }
      else if (obj is string)
      {
        Console.WriteLine("String parameter");
      }
    }
}

Running this program produces the following output:

Integer parameter

Float parameter

String parameter

The sizeof Operator

The sizeofoperator returns the size in bytes of the specified value type as illustrated by the following code:

using System;
public class Size
{
  public static void Main()
  {
    unsafe
    {
      Console.WriteLine("The size of short is {0}.", sizeof(short));
      Console.WriteLine("The size of int is {0}.", sizeof(int));
      Console.WriteLine("The size of double is {0}.",sizeof(double));
    }
  }
}

Note that the code containing the sizeof operator has been placed in an unsafe block. This is because the sizeof operator is considered an unsafe operation due to its accessing memory directly. For more information on unsafe code, see Safe And Unsafe Code.

typeof and GetType

The typeofoperator returns the type of the class passed to it as aSystem.Type object. The GetType() method is related, and returns the run-time type of a class or an expression. typeof and GetType() can be used in conjunction with reflection to find information about an object dynamically, as in the following example:

using System;
using System.Reflection;

public class Customer
{
    string name;

    public string Name
    {
      set
      {
        name = value;
      }
          
      get
      {
        return name;
      }
    }
}

public class TypeTest
{
    public static void Main()
    {
      Type typeObj = typeof(Customer);

      Console.WriteLine("The Class name is {0}",
         typeObj.FullName);
      
     // Or use the GetType() method:
      //Customer obj = new Customer();
      //Type typeObj = obj.GetType();

      Console.WriteLine("/nThe Class Members/n=================/n ");
      MemberInfo[] class_members = typeObj.GetMembers();
      foreach (MemberInfo members in class_members)
      {
        Console.WriteLine(members.ToString());
      }

      Console.WriteLine("/nThe Class Methods/n=================/n");
      MethodInfo[] class_methods = typeObj.GetMethods();
      foreach (MethodInfo methods in class_methods)
      {
        Console.WriteLine(methods.ToString());
      }
    }
}

If you run this program, it will produce output something like this:

The Class name is Customer

The Class Members
=================

Int32 GetHashCode()
Boolean Equals(System.Object)
System.String ToString()
Void set_Name(System.String)
System.String get_Name()
System.Type GetType()
Void .ctor()
System.String Name

The Class Methods
=================

Int32 GetHashCode()
Boolean Equals(System.Object)
System.String ToString()
Void set_Name(System.String)
System.String get_Name()
System.Type GetType()

This shows us the members that all classes inherit from System.Object, as well as revealing the way that C# represents get and set propertyaccessors as get_xxx() andset_xxx() methods internally.

In the next example, we useGetType() to find the type of an expression at run time:

using System;
public class TypeTest
{
  public static void Main()
  {
    int radius = 8;
    Console.WriteLine("Calculated area is = {0}",
                             radius * radius * System.Math.PI);
    Console.WriteLine("The result is of type {0}",
                             (radius * radius * System.Math.PI).GetType());
  }
}

The output of this program tells us that the result is of type System.Double, which is chosen because

System.Math.PIis of this type.

Calculated area is = 201.061929829747
The result is of type System.Double

Flow Control

Flow control statements are very similar in both languages, but there are some minor differences to cover in this section.

Branching Statements

Branching statements change the flow of program execution at run time according to certain conditions.

if, else, and else if

These are identical in both languages.

The switch Statement

In both languages, theswitch statement provides conditional multiple branching operations. There is a difference though in that Java allows you to "fall through" a case and execute the next case unless you use a break statement at the end of the case. C# however requires the use of either a break or a goto statement at the end of each case, and if neither is present, the compiler produces the following error:

Control cannot fall through from one case label to another.

Beware though, that where a case doesn't specify any code to execute when that case is matched, control will fall through to the subsequent case. When usinggoto in a switch statement, we can only jump to another case block in the same switch. If we want to jump to thedefault case, we would use goto default; otherwise we'd use gotocase cond; wherecond is the matching condition of the case we wish to jump to. Another difference to Java's switch is that in Java, we can only switch on integer types, while C# lets us switch on a string variable.

For example, the following would be valid in C#, but not in Java:

switch (args[0])
{
  case "copy":
    ...
    break;
  case "move":
    ...
    goto case "delete";
    break;
  case "del":
  case "remove":
  case "delete":
    ...
    break;
  default:
    ...
    break;
}

The Return of goto

In Java, gotois a reserved keyword that is not implemented. However, we can use labeled statements with break or continue to achieve a similar purpose as goto

C# does allow thegoto statement to jump to a labeled statement. Note though that in order to jump to a particular label, thegoto statement must be within the scope of the label. In other words, goto may not be used to jump into a statement block (although it can jump out of one), to jump out of a class, or to exit the finally block in try...catchstatements. Be aware though that goto is discouraged in most cases, as it contravenes good object-oriented programming practice.

Looping Statements

Looping statements repeat a specified block of code until a given condition is met.

for Loops

The syntax and operation of for loops is the same in both languages:

for (initialization; condition; expression)
  statement;

foreach Loops

C# introduces a new loop type called the foreach loop (similar to Visual Basic's For Each). The foreach loop allows iterating through each item in a container class (ex: arrays) that supports theIEnumerable interface. The following code illustrates the use of the foreach statement to output the contents of an array:

public static void Main()
{
  int[] arr1= new int[] {1,2,3,4,5,6};
  foreach ( int i in arr1)
  {
    Console.WriteLine("Value is {0}", i);
  }
}

We look at arrays in C# in more detail in the Arrays in C# section.

while and do...while Loops

The syntax and operation ofwhile and do...while statements are the same in both the languages:

while (condition)
{
  //statements
}

As usual, don't forget the trailing ; in do...while loops:

do
{
  //statements
}
while(condition);

Class Fundamentals

Access Modifiers

Modifiers are pretty much the same as those in Java, with several small differences that we will cover here. Each member of a class or the class itself may be declared with an access modifier to define the scope of permitted access. Classes that are not declared inside other classes can only specify the public or internal modifiers, while nested classes, like other class members, can specify any of the following five:

  • public – visible to all
  • protected – visible only from derived classes
  • private – visible only within the given class
  • internal – visible only within the same assembly
  • protectedinternal – visible only to the current assembly or types derived from the containing class
The public, protected, and private Modifiers

A public modifier makes the member available anywhere both inside and outside the class. A protected modifier indicates that access is limited to within the containing class or classes derived from it. A private modifier means that access is only possible from within the containing type.

The internal Modifier

An internal item may only be accessed within the current assembly. An assembly in the .NET world equates roughly to Java's JAR file; it represents the building blocks from which other programs can be constructed.

The protected internal Modifier

A protected internal item is visible only to the current assembly or types derived from the containing class.

In C#, the default access modifier is private, while Java's default is package scope.

The sealed Modifier

A class with the sealed modifier on its class declaration can be thought of as directly opposite to an abstract class – it cannot be inherited. We might mark a class as sealed to prevent other classes overriding its functionality. Naturally, a sealed class cannot be abstract. Also note that structs are implicitly sealed; therefore, they cannot be inherited. The sealed modifier is equivalent to marking a class with the final keyword in Java.

The readonly Modifier

To define a constant in C#, we use the const or readonly modifier in place of Java's final keyword. The distinguishing factor between the two modifiers in C# is that const items are dealt with at compile time, while readonly fields are set up at runtime. This can allow us to modify the expression used to determine the value of a readonly field at runtime.

This means that assignment to readonly fields may occur in the class constructor as well as in the declaration. For example, the following class declares a readonly variable called IntegerVariable that is initialized in the class constructor:

using System;
public class ReadOnlyClass
{
  private readonly int IntegerConstant;

  public ReadOnlyClass ()
  {
    IntegerConstant = 5;
  }

// We get a compile time error if we try to set the value of the readonly
  // class variable outside of the constructor
  public int IntMember
  {
    set
    {
      IntegerConstant = value;
    }
    get
    {
      return IntegerConstant;
    }
  }

  public static void Main(string[] args)
  {
    ReadOnlyClass obj= new ReadOnlyClass();

    // We cannot perform this operation on a readonly field
    obj.IntMember = 100;
    Console.WriteLine("Value of IntegerConstant field is {0}",
                             obj.IntMember);
  }
}

Note that if a readonly modifier is applied to a static field, it should be initialized in the static constructor of the class.

The Main() Method

Every C# application must contain one, and only one, Main() method specifying where program execution is to begin. Note that in C#, we capitalizeMain() while Java uses lowercasemain().

Main() can only return int or void, and has an optional string array argument to represent command line parameters:

static int Main (string[] args)
{
  ...
  return 0;
}

The string array parameter that contains any command-line arguments passed in works just as in Java. Thus, args[0] specifies the first command-line parameter, args[1] denotes the second parameter,

and so on. Unlike C++, theargs array does not contain the name of the EXE file.

Other Methods

When you pass parameters to a method, they may be passed by value or by reference. Value parameters simply take the value of any variable for use in the method, and hence the variable value in the calling code is not affected by actions performed on the parameters within a method.

Reference parameters on the other hand point to a variable declared in the calling code, and thus methods will modify the contents of that variable when passed by reference.

Passing by Reference

In both Java and C#, method parameters that refer to an object are always passed by reference, while primitive data type parameters are passed by value.

In C#, all parameters are passed by value by default. To pass by reference, we need to specify one of the keywords ref or out. The difference between these two keywords is in the parameter initialization. A ref parameter must be initialized before use, while an out parameter does not have to be explicitly initialized before being passed and any previous value is ignored.

Be aware that when reference types are used as parameters for a method, the reference is itself passed by value. However, the reference still points to the same object in memory, and so changes made to the object's properties will persist after the method exits. But, as the reference is itself passed by value, should it be changed inside the method to point to a different object, or even a new one, the reference would be restored to point to the original object once the method completes, even if the original object were unassigned

The ref Keyword

We specify this keyword on a parameter when we want the called method to permanently change the value of variables used as parameters. What happens is that rather than passing the value of a variable used in the call, a reference to the variable itself is passed. The method then works on the reference, so that changes to the parameter during the method's execution are persisted to the original variable used as a parameter to the method.

The following code illustrates this in the Add() method, where the secondint parameter is passed by reference with theref keyword:

using System;
public class RefClass
{
  public static void Main(string[] args)
  {
    int total = 20;
    Console.WriteLine("Original value of 'total': {0}", total);

    // Call the Add method
    Add (10, ref total);

    Console.WriteLine("Value after Add() call: {0}", total);
  }



  public static void Add (int i, ref int result)
  {
    result += i;
  }
}

The output of this simple example demonstrates that changes made to the result parameter are reflected in the variable, total, used in the Add()call:

Original value of 'total': 20
Value after Add() call: 30

This is because the result parameter references the actual memory location occupied by the total variable in the calling code. Be aware that a property of a class is not a variable, and cannot be used directly as a ref parameter.

Note that the ref keyword must precede the parameter when the method is called, as well as in the method declaration.

The out Keyword

The outkeyword has a very similar effect to the ref keyword, and modifications made to a parameter declared using out will be visible outside the method. The two differences fromref are that any initial value of an outparameter is ignored within the method, and secondly that anout parameter must be assigned to during the method:

using System;
public class OutClass
{
  public static void Main(string[] args)
  {
    int total = 20;
    Console.WriteLine("Original value of 'total': {0}", total);

    Add (33, 77, out total);

    Console.WriteLine("Value after Add() call: {0}", total);
  }

  public static void Add (int i, int j, out int result)
  {
    // The following line would cause a compile error
    // Console.WriteLine("Initial value inside method: {0}", result);
    result = i + j;
  }
}

In this case, the third parameter to the Add() method is declared with theout keyword, and calls to the method also require theout keyword for that parameter. The output will be:

Original value of 'total': 20
Value after Add() call: 110

So, to sum up, use theref keyword when you want a method to modify an existing variable, and use the out keyword to return a value produced inside the method. It is generally used in conjunction with the method's return value when the method produces more than one result value for the calling code.

Using an Indeterminate Number of Parameters

C# allows us to send a variable number of parameters to a method by specifying theparams keyword when the method is declared. The argument list can contain regular parameters also, but note that the parameter declared with the params keyword must come last. It takes the form of a variable length array, and there can be only one params parameter per method.

When the compiler tries to resolve a method call, it looks for a method whose argument list matches the method called. If no method overload that matches the argument list can be found, but there is a matching version with aparams parameter of the appropriate type, then that method will be called, placing the extra arguments in an array.

The following example demonstrates this idea:

using System;

public class ParamsClass
{
  public static void Main(string[] args)
  {
    Average ("List One", 5,10,15);
    Average ("List Two", 5,10,15,20,25,30);
  }

  public static void Average (string title, params int[] values)
  {
    int Sum = 0;
    Console.Write("Average of {0}: ", title);
    for (int i = 0; i < values.Length; i++)
    {
      Sum += values[i];
      Console.Write(values[i] + ", ");
    }
    Console.WriteLine(": {0}", (float)Sum/values.Length);
  }
}

In the above example, the method Average is declared with a params parameter of type integer array, letting us call it with any number of arguments. The output is shown here:

Average of List One: 5, 10, 15, : 10
Average of List Two: 5, 10, 15, 20, 25, 30, : 17.5

Note that we can specify aparams parameter of type Object if we wish to allow indeterminate parameters of different types.

Properties

In C#, a property is a named member of a class, struct, or interface offering a neat way to access private fields through what are called the get and set accessor methods.

The following code snippet declares a property called Species for the classAnimal, which abstracts access to the private variable called name

public class Animal
{
  private string name;

  public string Species
  {
    get
    {
      return name;
    }
    set
    {
      name = value;
    }
  }
}

Often, the property will have the same name as the internal member that it accesses, but with a capital initial letter (such as Name in the above case) or the internal member will have an _ prefix. Also, note the implicit parameter called value used in the set accessor – this has the type of the underlying member variable.

Accessors are in fact represented internally as get_X() and set_X() methods in order to maintain compatibility with the .NET languages which do not support accessors (as shown in the screenshot in the typeOfand GetType() section earlier in this document). Once a property is defined, it's then very easy to get or set its value:

Animal animal = new Animal()

// Set the property
animal.Species = "Lion";

// Get the property value
string str = animal.Species;

If property only has a get accessor, it is a read-only property. If it only has a set accessor, it is a write- only property. If it has both, it is a read-write property.

Structs

C# supports thestruct keyword, another item that originates in C but is not available in Java. You can think of a structas a lightweight class. It can contain constructors, constants, fields, methods, properties, indexers, operators, and nested types in much the same way as a class. structsdiffer from classes in that they cannot be abstract and do not support implementation inheritance. The important difference with a class is that structs are value types, while classes are reference types. There are some differences in the way constructors work for structs. In particular, the compiler always supplies a default no-parameter constructor, which you are not permitted to replace.

In the following example, we initialize a struct with the newkeyword and also by initializing the members of an instance:

using System;
public struct CustomerStruct
{
public int ID;
public string name;

public CustomerStruct(int customerID, string customerName)
{
ID = customerID;
name = customerName;
}
}

class TestClass
{
public static void Main(string[] args)
{
// Declare a CustomerStruct using the default constructor
CustomerStruct customer = new CustomerStruct();

Console.WriteLine("Struct values before initialization");
Console.WriteLine("ID = {0}, Name = {1}", customer.ID,
customer.name);
customer.ID = 100;
customer.name = "Robert";

Console.WriteLine("Struct values after initialization");
Console.WriteLine("ID = {0}, Name = {1}", customer.ID,
customer.name);
}
}

When we compile and run the above code, its output shows that struct variables are initialized by default. The int variable is initialized to 0, and the string variable to an empty string:

struct values before initialization

ID = 0, Name =

struct values after initialization

ID = 100, Name = Robert

Note that when we declarecustomer using the alternate notation,CustomerStruct customer, its member variables would not be initialized, and trying to use them before setting them to values would generate a compile time error.

Arrays in C#

Arrays are ordered collections of items of the same data type that are accessed using the array name in conjunction with the offset from the start of the array of the desired item. There are some important differences in how arrays are declared and used in C# compared to Java that I will cover in this section.

The One-Dimensional Array

A one-dimensional array stores a fixed number of items in a linear fashion, requiring just a single index value to identify any one item.

In C#, the square brackets in the array declaration must follow the data type, and may not appear after the variable name as permitted in Java. Thus, an array of type integers is declared using following syntax:

int[] MyArray;

and the following declaration is invalid in C#:

int MyArray[];

Once we have declared an array, we use the new keyword to set its size, just as in Java:

int[] MyArray; // declares the array reference MyArray = new int[5]; // creates a 5 element integer array

We then access elements in a one-dimensional array using identical syntax to Java, noting that C# array indices are also zero-based:

MyArray [4] // accesses the last element in the array

Initialization

Array elements may be initialized at creation using the same syntax as in Java:

MyArray = new int[5] {1, 2, 3, 4, 5};

Unlike Java, the number of initializers must match the array size exactly.

We can use this feature to declare and initialize a C# array in a single line:

int[] TaxRates = {0, 20, 23, 40, 50};

This syntax creates an array of size equal to the number of initializers.

Initializing in a Program Loop

The other way to initialize an array in C# is to use the foreach loop. The loop below sets each element of an array to zero:

int[] MyLittleArray = new int[5]; foreach (int i in MyLittleArray) { MyLittleArray[i] = 0; }

Jagged Arrays

Both C# and Java support creating jagged, or non-rectangular, arrays, where each row contains a different number of columns. For instance, the following jagged array has four entries in the first row, and three in the second:

int[][] JaggedArray = new int[2][]; JaggedArray[0] = new int[4]; JaggedArray[1] = new int[3];

Multi-Dimensional Arrays

C# allows us to create regular multi-dimensional arrays that can be thought of as a matrix of values of the same type. While both Java and C# support jagged arrays, C# also supports multi-dimensional arrays or arrays of arrays. We'll look at jagged arrays in a moment.

We declare a multi-dimensional rectangular array using following syntax:

int[,] My2DIntArray; float[,,,] My4DFloatArray;

whereMy2DintArray is the name by which every element can be accessed.

Note that the lineint[][] My2DintArray; has a different meaning in C#, as we shall see shortly.

Once declared, we allocate memory to the array like so:

int[,] My2DIntArray; // declares array reference My2DIntArray = new int[5,4]; // allocates space for 5x4 integers

Elements of the array are then accessed using the following syntax:

My2DIntArray [4,3] = 906;

As arrays are zero-based, this sets the element in the fifth column of the fourth row – the bottom right hand corner – to 906.

Initialization

Multi-dimensional arrays may be created, set up, and initialized in a single line by any of the following methods:

int[,] intArray = { {1,2,3}, {4,5,6} }; int[,] intArray = new int [2,3] { {1,2,3}, {4,5,6} }; int[,] intArray = new int [,] { {1,2,3}, {4,5,6} };

Initializing in a Program Loop

All the elements of an array may be initialized using a nested loop as shown here:

int[,] intArray = new int[5,4]; for(int i=0;i<5;i++) { for(int j=0;i<4;j++) { intArray[i,j] = j; } }

The System.Array Class

In .NET, arrays are implemented as instances of the System.Array class. This class provides several useful methods, such asSort() and Reverse().

The following program demonstrates how easy these methods are to work with. First, we reverse the elements of an array using theReverse()method of the Array class, and then we sort them with the Sort() method:

using System;

public class ArrayMethods
{
public static void Main()
{
// Create string array of size 5
string[] EmployeeNames = new string[5];
Console.WriteLine("Enter five employee names:");


// Read 5 employee names from user
for(int i=0;i<5;i++)
{
EmployeeNames[i]= Console.ReadLine();
}

// Print the array in original order
Console.WriteLine("/n** Original Array **");
foreach(string EmployeeName in EmployeeNames)
{
Console.Write("{0} ", EmployeeName);
}

//print the array in reverse order.
Console.WriteLine("/n/n** Values in Reverse Order **");
System.Array.Reverse(EmployeeNames);
foreach(string EmployeeName in EmployeeNames)
{
Console.Write("{0} ", EmployeeName);
}

//print the array in sorted order.
Console.WriteLine("/n/n** Values in Sorted Order **");
System.Array.Sort(EmployeeNames);
foreach(string EmployeeName in EmployeeNames)
{
Console.Write("{0} ", EmployeeName);
}

}
}

Here's some typical output for this program:

Enter five employee names:
Luca
Angie
Brian
Kent
Beatriz

** Original Array **
Luca Angie Brian Kent Beatriz

** Values in Reverse Order **
Beatriz Kent Brian Angie Luca

** Values in Sorted Order **
Angie Beatriz Brian Kent Luca

Inheritance and Derived Classes

We can extend the functionality of an existing class by creating a new class that derives from the existing class. The derived class inherits the properties of the base class, and we can add or override methods and properties as required.

In C# both inheritance and interface implementation are defined by the :operator, equivalent to extends and implements in Java. Note that the base class should always be leftmost in the class declaration.

Like Java, C# does not support multiple inheritance, meaning that classes can't inherit from more than one class. We can however use interfaces for that purpose in the same way as in Java, as we'll see in the next section.

The following code defines a class called Point with two private member variablesx, y representing the position of the point. These variables are accessed through properties calledX and Y respectively:

public class Point
{
private int x, y;
public Point()
{
x = 0;
y = 0;
}

public int X
{
get
{
return x;
}
set
{
x = value;
}
}
public int Y
{
get
{
return y;
}
set
{
y = value;
}
}
}

We would derive a new class, called ColorPoint say, from the Point class like so:

public class ColorPoint : Point

ColorPointthen inherits all the fields and methods of the base class, to which we can add new ones to provide extra features in the derived class according to our needs. In this case, we add a private

member and accessors to add color to the point:

using System.Drawing;

public class ColorPoint : Point
{
private Color screenColor;


public ColorPoint()
{
screenColor = Color.Red;
}

public Color ScreenColor
{
get
{
return screenColor;
}
set
{
screenColor = value;
}
}
}
Note that the constructor of the derived class implicitly calls the constructor for the base class (or the superclass in Java terminology). In inheritance, all base class constructors are called before the derived class's constructors in the order that the classes appear in the class hierarchy.

Typecasting to a Base Class

As in Java, we can't use a reference to a base class to access the members and methods of a derived class even if the base class reference may contain a valid reference to an object of the derived type.

We can reference a derived class with a reference to the derived type implicitly:

ColorPoint clrpt = new ColorPoint(); Point pt = clrpt;

In this code, the base class reference, pt, contains a copy of theclrpt reference.

The base Keyword

We can access base class members in a subclass even when those base members are overridden in the superclass using the base keyword. For instance, we could create a derived class which contains a method with the same signature as in the base class. If we prefaced that method with the new keyword, we indicate that this is an all-new method belonging to the derived class. We could still provide a method for accessing the original method in the base class with the base keyword.

For instance, say our basePoint class had a method called invert(),which swaps the x and y coordinates over. We could provide a substitute for this method in our derivedColorPoint class with code like this:

public new void invert() { int holding = X; X = Y; Y = holding; screenColor = Color.Gray; }

As you can see, this method swaps x and y, and then sets the point's color to gray. We could provide access to the base implementation for this method by creating another method inColorPoint such as this one:

public void baseInvert() { base.invert(); }

We would then invoke the base method on a ColorPoint object by calling thebaseInvert() method.

ColorPoint clrpt = new ColorPoint(); clrpt.baseInvert();

Remember that we would get the same effect if we assigned a reference to the base class to an instance of ColorPoint, and then accessed its methods:

Point pt = clrpt; pt.invert();

Selecting Constructors

Base class objects are always constructed before any deriving class. Thus the constructor for the base class is executed before the constructor of the derived class. If the base class has more than one constructor, the derived class can decide the constructor to be called. For example, we could modify our Point class to add a second constructor:

public class Point { private int x, y; public Point() { x = 0; y = 0; } public Point(int x, int y) { this.x = x; this.y = y; } }

We could then change theColorPoint class to use a particular one of the available constructors using the base keyword:

public class ColorPoint : Point { private Color color; public ColorPoint(int x, int y) : base (x, y) { color = Color.Red; } }

In Java, this functionality is implemented using the super keyword.

Method Overriding

A derived class may override the method of a base class by providing a new implementation for the declared method. An important distinction between Java and C# is that by default Java methods are marked as virtual, while in C# methods must be explicitly marked as virtual using the virtual modifier. Property accessors as well as methods can be overridden in much the same way.

Virtual Methods

A method that is to be overridden in a derived class is declared with thevirtual modifier. In a derived class, the overridden method is declared using the override modifier.

The overridemodifier denotes a method or a property of a derived class that replaces one with the same name and signature in the base class. The base method, which is to be overridden must be declared asvirtual, abstract, oroverride: it is not possible to override a non-virtual or static method in this way – see the next heading for that case. Both the overridden and the overriding method or property must have the same access level modifiers.

The following example shows a virtual method called StepUp that is overridden in a derived class with the override modifier:

using System;
public class CountClass
{
public int count;

// Constructor
public CountClass(int startValue)
{
count = startValue;
}

public virtual int StepUp()
{
return ++count;
}
}

class Count100Class : CountClass
{
// Constructor
public Count100Class(int x) : base(x)
{
}

public override int StepUp()
{
return ((base.count) + 100);
}

public static void Main()
{
CountClass counter = new CountClass(10);
CountClass bigCounter = new Count100Class(10);
Console.WriteLine("Value of count in base class = {0}",
counter.StepUp());
Console.WriteLine("Value of count in derived class = {0}",
bigCounter.StepUp());
}
}

When we run this code, we see that the derived class's constructor uses the method body given in the base class, letting us initialize the count member without duplicating that code. Here's the output we get:

Value of count in base class = 11 Value of count in derived class = 110

Abstract Classes

An abstract class declares one or more methods or properties as abstract. Such methods do not have an implementation provided in the class that declares them, although an abstract class can also contain non-abstract methods, that is, methods for which an implementation has been provided. An abstract class cannot be instantiated directly, but only as a derived class. Such derived classes must provide implementations for all abstract methods and properties, using theoverride keyword, unless the derived member is itself declared abstract.

The following example declares an abstract Employee class. We also create a derived class called Manager that provides an implementation of the abstract show() method defined in the Employee class:

using System;
public abstract class Employee
{
// abstract show method
public abstract void show();
}

// Manager class extends Employee
public class Manager: Employee
{
string name;
public Manager(string name)
{
this.name = name;
}

//override the show method
public override void show()
{
Console.WriteLine("Name : " + name);
}
}

public class CreateManager
{
public static void Main(string[] args)
{
// Create instance of Manager and assign it to an Employee reference
Employee temp = new Manager("John Chapman");

// Call show method. This will call the show method of the Manager class
temp.show();
}
}

This code invokes the implementation of show() provided by theManager class, and prints the employee name on screen.

Interfaces

An interface is a sort of "skeleton class", containing method signatures but no method implementations. In this way, interfaces are like abstract classes that contain only abstract methods. C# interfaces are very similar to Java interfaces, and work in very much the same way.

All the members of an interface are public by definition, and an interface cannot contain constants, fields (private data members), constructors, destructors, or any type of static member. The compiler will generate an error if any modifier is specified for the members of an interface.

We can derive classes from an interface in order to implement that interface. Such derived classes must provide implementations for all the interface's methods unless the derived class is declared abstract.

An interface is declared identically to Java. In an interface definition, a property indicates only its type, and whether it is read-only, write-only, or read/write by get and set keywords alone. The interface below declares one read-only property:

public interface IMethodInterface
{
// method signatures
void MethodA();
int MethodB(float parameter1, bool parameter2);

// properties
int ReadOnlyProperty
{
get;
}
}

A class can inherit from this interface, using a colon in place of Java's implements keyword. The implementing class must provide definitions for all methods, and any required property accessors:

public class InterfaceImplementation : IMethodInterface
{
// fields
private int count = 0;
private int ID;

// implement methods defined in interface
public void MethodA()
{
...
}

public int MethodB(float parameter1, bool parameter2)
{
...
return integerVariable;
}

public int ReadOnlyProperty
{
get
{
return count;
}
}

// add extra methods if required

}
Implementing Multiple Interfaces

A class may implement multiple interfaces using the following syntax:

public class MyClass : interfacename1, interfacename2, interfacename3

If a class implements more than one interface where there is ambiguity in the names of members, it is resolved using the full qualifier for the property or method name. In other words, the derived class can resolve the conflict by using the fully qualified name for the method to indicate to which interface it belongs, as inIMethodInterface.MethodA.

Operator Overloading

Like C++, C# allows us to overload operators for use on our own classes. This makes it possible for a user-defined data type to look as natural and be as logical to use as a fundamental data type. For example, we might create a new data type called Complex to represent a complex number, and provide methods that perform mathematical operations on such numbers using the standard arithmetic operators, such as using the + operator to add two complex numbers.

To overload an operator, we write a function that has the name operator followed by the symbol for the operator to be overloaded. For instance, this is how we would overload + operator:

public static complex operator+(complex lhs, complex rhs)

All operator overloads are static methods of the class. Also be aware that if you overload the equality

(==) operator, you must overload the inequality operator (!=) as well.

The full list of operators that can be overloaded is:

  • Unary operators: +, -, !, ~, ++, --, true, false

  • Binary operators: +, -, *, /, %, &, |, ^, <<, >>, ==, !=, >, <, >=, <=

The next example creates a Complex class that overloads the + and - operators:

using System;
public class complex
{
private float real;
private float img;

public complex(float p, float q)
{
real = p;
img = q;
}

public complex()
{
real = 0;
img = 0;
}

public void Print()
{
Console.WriteLine("{0} + {1}i", real, img);
}

// Overloading '+' operator
public static complex operator+(complex lhs, complex rhs)
{
complex sum = new complex();
sum.real = lhs.real + rhs.real;
sum.img = lhs.img + rhs.img;
return (sum);
}

// Overloading '-' operator
public static complex operator-(complex lhs, complex rhs)
{
complex result = new complex();
result.real = lhs.real - rhs.real;
result.img = lhs.img - rhs.img;
return (result);
}
}

This class allows us to create and manipulate two complex numbers with code such as this:

using System;

public class ComplexClass
{
public static void Main(string[] args)
{
// Set up complex numbers
complex A = new complex(10.5f,12.5f);
complex B = new complex(8.0f,4.5f);
complex C;

// Print object A and B
Console.Write("Complex Number A: ");
A.Print();
Console.Write("Complex Number B: ");
B.Print();

// Add A and B, print result
C = A + B;
Console.Write("/nA + B = ");
C.Print();

// Subtract A and B, print result
C = A - B;
Console.Write("A - B = ");
C.Print();
}
}

As the program demonstrates, we can now use the plus and minus operators on objects belonging to our complex class quite intuitively. Here is the output we would get:

Complex Number A: 10.5 + 12.5i Complex Number B: 8 + 4.5i A + B = 18.5 + 17i A - B = 2.5 + 8i

Java does not support operator overloading, although internally it overloads the+ operator for string concatenation.

Exceptions

Exception handling in C# is very similar to that of Java.

Whenever something goes critically wrong during execution of a program, the .NET runtime creates an Exception object detailing the error. In .NET, Exception is the base class for all the exception classes. There are two categories of exceptions that derive from the Exception base class,System.SystemException andSystem.ApplicationException. All types in the System namespace derive from System.SystemException while user-defined exceptions should derive fromSystem.ApplicationException to differentiate between runtime and application errors. Some common Systemexceptions include:

  • IndexOutOfRangeException – an index greater than the size of an array or collection was used
  • NullReferenceException – a property or method of a reference has been used before that reference has been set to a valid instance
  • ArithmeticException – thrown when an operation results in overflow or underflow
  • FormatException – an argument or operand was in an incorrect format

As in Java, when we have code that is liable to cause an exception, we place that code within a try block. One or more catchblocks immediately after provide the error handling, and we can also use a finally block for any code that we want to execute whether an exception is thrown or not.

Note that when using multiple catch blocks, the exceptions caught must be placed in order of increasing generality as only the first catch block that matches the thrown exception will be executed. The C# compiler will enforce this while the Java compiler will not.

Also, C# doesn't require an argument for a catch block as Java does; in the absence of an argument, the catch block applies to any Exception class.

For example, while reading from a file, you may encounter a FileNotFoundExceptionor an IOException, and we would want to place the more specific FileNotFoundException handler first:

try
{
// Code to open and read a file
}
catch (FileNotFoundException fe)
{
// Handle file not found exception first
}
catch (IOException ioe)
{
// Now handle any other IO exceptions
}
catch
{
// This block will catch all other exceptions
}
finally
{
// Executed whether or not an exception occurs, often to release resources
}

We can create our own exception classes by deriving from Exception. For example, the following code creates an InvalidDepartmentException class that we might throw if, say, the department given for a new employee record is invalid. The class constructor for our user-defined exception calls the base class constructor using thebase keyword, sending an appropriate message:

public class InvalidDepartmentException : System.Exception
{
public InvalidDepartmentException(string Department) : base(
"Invalid Department: " + Department){ }
}

We could then throw an exception of this type with code such as this:

if (!(Department == "Sales" | Department == "Marketing"))
{
throw new InvalidDepartmentException(Department);
}

Note that C# does not support checked exceptions. In Java these are declared using thethrows keyword, to specify that a method may throw a particular type of exception which must be handled by the calling code. For more information on why C# doesnt support checked exceptions, read this interview with Anders Hejlsberg.

Advanced C# Techniques

Indexers

Indexers provide a way to access a class or struct in the same way as an array. For example, we may have a class that represents a single department in our company. The class could contain the names of all employees in the department, and indexers could allow us to access these names like this:

myDepartment[0] = "Fred"; myDepartment[1] = "Barney";

and so on. Indexers are enabled by defining a property with the following signature in the class definition:

public type this [int index]

We then provideget and set methods as for a normal property, and it is these accessors that specify what internal member is referred to when the indexer is used.

In the following simple example, we create a class called Department that uses indexers to access the employees in that department, internally represented as an array of strings:

using System;

public class Department
{
private string name;
private const int MAX_EMPLOYEES = 10;
private string [] employees = new string [MAX_EMPLOYEES];

public Department(string deptName)
{
name = deptName;
}

public string this [int index]
{
get
{
if (index >= 0 && index < MAX_EMPLOYEES)
{
return employees[index];
}
else
{
throw new IndexOutOfRangeException();
//return "Error";
}
}
set
{
if (index >= 0 && index < MAX_EMPLOYEES)
{
employees[index] = value;
}
else
{
throw new IndexOutOfRangeException();
//return "Error";
}
}
}

// Other methods and properties as usual
}

We can then create an instance of this class and access it as shown below:

using System;

public class SalesDept
{
public static void Main(string[] args)
{
Department sales = new Department("Sales");
sales[0] = "Nikki";
sales[1] = "Becky";
Console.WriteLine("The sales team is {0} and {1}", sales[0],
sales[1]);
}
}

For more information on indexers, see Indexer.

Attributes

C# introduces a new mechanism for adding declarative information about types called attributes. Extra information about a type is placed inside declarative tags that precede the type definition. The examples below show how you can leverage .NET Framework attributes to decorate a class or method.

In the example below, theGetTime method is marked as an XML Web service by adding the WebMethod attribute.

  using System;
using System.Web.Services;

public class Utilities : WebService
{

[WebMethod]
public string GetTime()
{
return DateTime.Now.ToShortTimeString();
}
}

By adding theWebMethod attribute, the .NET Framework will now automatically take care of the XML/SOAP interchange necessary to call this function. Calling this web service retrieves the following value:

<?xml version="1.0" encoding="utf-8" ?> <string xmlns="http://tempuri.org/">7:26 PM</string>

In the example below, theEmployee class is marked as Serializableby adding the Serializable() attribute. While the salary field is marked as public, it will not be serialized as it is marked with the NonSerialized()attribute.

using System; [Serializable()] public class Employee { public int ID; public string Name; [NonSerialized()] public int Salary; }

For information on creating custom attributes, see Creating Custom Attributes.

Delegates

Languages such as C++, Pascal, and others support the concept of function pointers that permit us to choose which function we wish to call at run time.

Java does not provide any construct with the functionality of a function pointer, but C# does, through the System.Delegate class. A delegate instance encapsulates a method that is a callable entity.

For instance methods, the delegate consists of an instance of the containing class and a method on the instance. For static methods, a callable entity consists of a class and a static method on the class. Thus, a delegate may be used to invoke a function of any object, and delegates are object-oriented, type- safe, and secure.

There are three steps when defining and using delegates:

  • Declaration

  • Instantiation

  • Invocation

We declare a delegate with the following syntax:

delegate void myDelegate();

This delegate can then be used to reference any function that returns void and does not take any arguments.

Similarly, to create a delegate for any function that takes a string parameter and returns a long, we would use the following syntax:

delegate long myDelegate(string mystring);

We could then assign this delegate to any method with this signature, like so:

myDelegate operation = new myDelegate(methodName);

Re-assigning Delegates

Delegate objects are immutable, that is, the signature they match cannot be changed once set. However, we can point to another method as long as both have the same signature. For instance:

delegate myDelegate(int a, int b) myDelegate operation = new myDelegate(Add);

operation = new myDelegate(Multiply);

Here, we reassignoperation to a new delegate object so thatoperation then invokes the Multiplymethod. We can only do this if both Add() andMultiply() have the same signature.

Invoking Delegates

Invoking a delegate is fairly straightforward, simply substituting the name of the delegate variable for the method name:

delegate long myDelegate(int i, int j); myDelegate operation = new myDelegate(Add);

long lresult = operation(10, 20);

This invokes theAdd method with values 10 and 20, and returns a long result that is assigned to variable lresult.

Let's create a quick program to illustrate the creation, instantiation, and invocation of a delegate:

using System;

public class DelegateClass
{
delegate long myDelegate (int i, int j);

public static void Main(string[] args)
{
myDelegate operation = new myDelegate(MathClass.Add);

Console.WriteLine("Call to Add method through delegate");
long l = operation(10, 20);
Console.WriteLine("Sum of 10 and 20 is " + l);

Console.WriteLine("Call to Multiply method thru delegate");
operation = new myDelegate(MathClass.Multiply);
l = operation(1639, 1525);
Console.WriteLine("1639 multiplied by 1525 equals " + l);

}
}

public class MathClass
{

public static long Add (int i, int j)
{
return (i+j);
}

public static long Multiply (int i, int j)
{
return (i*j);
}

}

The output we will get is this:

Call to Add method through delegate Sum of 10 and 20 is 30 Call to Multiply method through delegate 1639 multiplied by 1525 equals 2499475

As mentioned earlier, a delegate instance must contain an object reference. We got round this in the example above by declaring our methods as static, which means there's no need to specify an object reference ourselves. If a delegate refers to an instance method however, the object reference must be given like so:

MathClass obj = new MathClass(); myDelegate operation = new myDelegate(obj.Power);

where Power is an instance method of MathClass. So, if MathClass's methods were not declared as

static, we would invoke them through a delegate like so:

using System;

public class DelegateClass
{
delegate long myDelegate(int i, int j);

public static void Main(string[] args)
{
MathClass mathObj = new MathClass();
myDelegate operation = new myDelegate(mathObj.Add);

Console.WriteLine("Call to Add method through delegate");
long l = operation(10, 20);
Console.WriteLine("Sum of 10 and 20 is " + l);

Console.WriteLine("Call to Multiply method thru delegate");
operation = new myDelegate(mathObj.Multiply);
l = operation(1639, 1525);
Console.WriteLine("1639 multiplied by 1525 equals " + l);
}
}

If you run this program, you'll get the same output as previously, when the methods were declared as static.

Delegates and Events

The .NET Framework also leverages delegates extensively for event handling tasks like a button click event in a Windows or Web application. While event handling in Java is typically done by implementing custom listener classes, C# developers can take advantage of delegates for event handling. An event is declared like a field with a delegate type, except that the keyword event precedes the event declaration. Events are typically declared public, but any accessibility modifier is allowed. The code below shows the declaration of a delegate and event.

public delegate void CustomEventHandler(object sender, EventArgs e);

public event CustomEventHandler CustomEvent;

Event delegates are multicast, which means that they can hold references to more than one event handling method. A delegate acts as an event dispatcher for the class that raises the event by maintaining a list of registered event handlers for the event. The example below shows how you can subscribe multiple functions to an event. The class EventClass contains the delegate, the event, and a method to invoke the event. Note that invoking an event can only be done from within the class that declared the event. The class TestClass can then subscribe to the event using the += operator and unsubscribe using the -= operator. When the InvokeEvent() method is called, it fires the event and any functions that have subscribed to the event will fire synchronously as shown in the code below.

using System;

class TestClass
{
static void Main(string[] args)
{
EventClass myEventClass = new EventClass();
// Associate the handler with the events
myEventClass.CustomEvent += new EventClass.CustomEventHandler(CustomEvent1);
myEventClass.CustomEvent += new EventClass.CustomEventHandler(CustomEvent2);
myEventClass.InvokeEvent();
myEventClass.CustomEvent -= new EventClass.CustomEventHandler(CustomEvent2);
myEventClass.InvokeEvent();
}

private static void CustomEvent1(object sender, EventArgs e)
{
Console.WriteLine("Fire Event 1");
}

private static void CustomEvent2(object sender, EventArgs e)
{
Console.WriteLine("Fire Event 2");
}
}

public class EventClass
{
public delegate void CustomEventHandler(object sender, EventArgs e);

//Declare the event using the delegate datatype
public event CustomEventHandler CustomEvent;

public void InvokeEvent()
{
CustomEvent(this, EventArgs.Empty);
}
}

The output of this program is:

Fire Event 1 Fire Event 2 Fire Event 1

Garbage Collection

In C and C++, many objects require the programmer to allocate them resources once declared, before the objects may be safely used. Releasing these resources back to the free memory pool once the object is done with is also the responsibility of the programmer. If resources are not released, the code is said to leak memory, as more and more resources are consumed needlessly. On the other hand, if resources are released prematurely, loss of data, the corruption of other memory areas, and null pointer exceptions can occur.

Both Java and C# prevent these dangers by independently managing the lifetime of all objects in use by an application.

In Java, the JVM takes care of releasing unused memory by keeping track of the references to allocated resources. Whenever the JVM detects that a resource is no longer referenced by a valid reference, the resource is garbage-collected.

In C#, garbage collection is handled by the Common Language Runtime (CLR) with similar functionality to that of the JVM. The CLR garbage collector periodically checks the memory heap for any unreferenced objects, and releases the resources held by these objects. For more information on Garbage Collection, see Garbage Collection: Automatic Memory Management in the Microsoft .NET Framework, and Garbage Collection—Part 2: Automatic Memory Management in the Microsoft .NET Framework.

Safe and Unsafe Code

A particularly interesting feature of C# is its support for non-type safe code. Normally, the CLR takes on the responsibility overseeing the behavior of IL (Intermediate Language) code, and prevents any questionable operations. However, there are times when we wish to directly access low-level functionality such as Win32 API calls, and we are permitted to do this, as long as we take responsibility for ensuring such code operates correctly. Such code must be placed inside unsafe blocks in our source code.

The unsafe Keyword

C# code that makes low-level API calls, uses pointer arithmetic, or carries out some other unsavory operation, has to be placed inside blocks marked with the unsafe keyword. Any of the following can be marked as unsafe:

  • An entire method
  • A code block in braces
  • An individual statement

The following example demonstrates the use of unsafe in all three of the above situations:

using System;

class UnsafeClass
{
unsafe static void PointyMethod()
{
int i=10;
int *p = &i;
Console.WriteLine("*p = " + *p);
string address = "Pointer p = " + int.Format((int) p, "X");
Console.WriteLine(address);
}

static void StillPointy()
{
int i=10;
unsafe
{
int *p = &i;
Console.WriteLine("*p = " + *p);
string address = "Pointer p = " + int.Format((int) p, "X");
Console.WriteLine(address);
}
}

static void Main()
{
PointyMethod();
StillPointy();
}
}

In this code, the entirePointyMethod() method is marked unsafe because the method declares and uses pointers. TheStillPointy()method marks a block of code as unsafe as this block once again uses pointers.

For more information about unsafe code, see Unsafe at the Limit.

The fixed Keyword

In safe code, the garbage collector is quite free to move an object during its lifetime in its mission to organize and condense free resources. However, if our code uses pointers, this behavior could easily cause unexpected results, so we can instruct the garbage collector not to move certain objects using the fixed keyword.

The following code shows the fixed keyword being used to ensure that an array is not moved by the system during the execution of a block of code in the PointyMethod() method. Note that fixed is only used within unsafe code:

public class FixedClass
{
public static void PointyMethod(char[] array)
{
unsafe
{
fixed (char *p = array)
{
for (int i=0; i&lt;array.Length; i++)
{
Console.Write(*(p+i));
}
}
}
}

static void Main ()
{
char[] array = { 'H', 'e', 'l', 'l', 'o' };
PointyMethod(array);
}
}

Summary

Though Microsoft and other vendors have introduced many languages for the .NET platform, C# is a language that closely resembles Java and is very well suited to developers wishing to migrate from J2EE to the .NET platform.

This document has compared and contrasted the two languages. In many ways, C# has the power of C++, the elegance of Java, and the ease of development of Visual Basic, and I hope that this document has demonstrated this.

To learn how to get started creating your first C# application, visit the Java Resource Center Getting Started Page.

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值