Advanced C# Techniques (C# vs Java)

C# provides some useful language features, such as indexers, attributes, and delegates, which enable advanced programming techniques.

Indexers

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

sales[0] = "Nikki";
sales[1] = "Becky";

Indexers are enabled by defining a property with the following signature, for example, in the class definition:

public string this [int index]  //indexer

You then provide get and set methods as you would 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, you create a class called Department that uses indexers to access the employees in that department, internally represented as an array of strings:

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

    public Department(string departmentName)  //constructor
    {
        name = departmentName;
    }

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

    // code for the rest of the class...
}

You can then create an instance of this class and access it as shown in the following code example:

class TestDepartment
{
    static void Main()
    {
        Department sales = new Department("Sales");

        sales[0] = "Nikki";
        sales[1] = "Becky";

        System.Console.WriteLine("The sales team is {0} and {1}", sales[0], sales[1]);
    }
}

The output is:

The sales team is Nikki and Becky

For more information, see Indexers (C# Programming Guide).

Attributes

C# provides a mechanism, called an attribute, for adding declarative information about types. Attributes are somewhat similar to the concept of annotations in Java. Extra information about a type is placed inside declarative tags that precede the type definition. The following examples show how you can use .NET Framework attributes to decorate a class or method.

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

public class Utilities : System.Web.Services.WebService
{
    [System.Web.Services.WebMethod]  // Attribute 
    public string GetTime()
    {
        return System.DateTime.Now.ToShortTimeString();
    }
}

Adding the WebMethod attribute makes the .NET Framework 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 following example, the Employee class is marked as serializable by adding the SerializableAttribute attribute. While the Salary field is marked as public, it will not be serialized as it is marked with the NonSerializedAttribute attribute.

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

For more information, see Creating Custom Attributes (C# Programming Guide).

Delegates

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

Java does not provide any construct with the functionality of a function pointer, but C# does. Through the use of the 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 can be used to invoke a function of any object, and delegates are object-oriented, type- safe, and secure.

There are three steps for defining and using delegates:

  • Declaration

  • Instantiation

  • Invocation

You declare a delegate with the following syntax:

delegate void Del1();

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, you would use the following syntax:

delegate long Del2(string s);

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

Del2 d;                // declare the delegate variable
d = DoWork;  // set the delegate to refer to the DoWork method

Where the signature of DoWork is:

public static long DoWork(string name)

Reassigning Delegates

Delegate objects are immutable; that is, the signature they match cannot be changed once set. However, you can point to another method as long as both have the same signature. In this example, you reassign d to a new delegate object so that d then invokes the DoMoreWork method. You can only do this if both DoWork and DoMoreWork have the same signature.

Del2 d;                    // declare the delegate variable
d = DoWork;      // set the delegate to refer to the DoWork method
d = DoMoreWork;  // reassign the delegate to refer to the DoMoreWork method

Invoking Delegates

Invoking a delegate is fairly straightforward. You simply substitute the name of the delegate variable for the method name. This invokes the Add method with values 11 and 22, and returns a long result that is assigned to variable sum:

Del operation;                 // declare the delegate variable
operation = Add;      // set the delegate to refer to the Add method 
long sum = operation(11, 22);  // invoke the delegate

The following illustrates the creation, instantiation, and invocation of a delegate:

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

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

class TestMathClass
{
    delegate long Del(int i, int j);  // declare the delegate type 

    static void Main()
    {
        Del operation;  // declare the delegate variable

        operation = MathClass.Add;       // set the delegate to refer to the Add method 
        long sum = operation(11, 22);             // use the delegate to call the Add method

        operation = MathClass.Multiply;  // change the delegate to refer to the Multiply method 
        long product = operation(30, 40);         // use the delegate to call the Multiply method

        System.Console.WriteLine("11 + 22 = " + sum);
        System.Console.WriteLine("30 * 40 = " + product);
    }
}

Output

11 + 22 = 33

30 * 40 = 1200

A delegate instance must contain an object reference. The previous example gets around this by declaring methods as static, which means there is no need to specify an object reference. If a delegate refers to an instance method, however, the object reference must be given as follows:

Del operation;                   // declare the delegate variable
MathClass m1 = new MathClass();  // declare the MathClass instance
operation = m1.Add;     // set the delegate to refer to the Add method

In this example, Add and Multiply are instance methods of MathClass. If the methods of MathClass are not declared as static, you invoke them through the delegate by using an instance of the MathClass, as follows:

public class MathClass
{
    public long Add(int i, int j)       // not static
    {
        return (i + j);
    }

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

class TestMathClass
{
    delegate long Del(int i, int j);  // declare the delegate type 

    static void Main()
    {
        Del operation;                   // declare the delegate variable
        MathClass m1 = new MathClass();  // declare the MathClass instance

        operation = m1.Add;       // set the delegate to refer to the Add method 
        long sum = operation(11, 22);      // use the delegate to call the Add method

        operation = m1.Multiply;  // change the delegate to refer to the Multiply method 
        long product = operation(30, 40);  // use the delegate to call the Multiply method

        System.Console.WriteLine("11 + 22 = " + sum);
        System.Console.WriteLine("30 * 40 = " + product);
    }
}

Output

This example provides the same output as the previous example in which the methods were declared as static.

11 + 22 = 33

30 * 40 = 1200

Delegates and Events

The .NET Framework also uses 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 following example shows the declaration of a delegate and event.

// Declare the delegate type: 
public delegate void CustomEventHandler(object sender, System.EventArgs e);

// Declare the event variable using the delegate type: 
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 following example 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 TestEvents 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 following example.

public class EventClass
{
    // Declare the delegate type: 
    public delegate void CustomEventHandler(object sender, System.EventArgs e);

    // Declare the event variable using the delegate type: 
    public event CustomEventHandler CustomEvent;

    public void InvokeEvent()
    {
        // Invoke the event from within the class that declared the event:
        CustomEvent(this, System.EventArgs.Empty);
    }
}

class TestEvents
{
    private static void CodeToRun(object sender, System.EventArgs e)
    {
        System.Console.WriteLine("CodeToRun is executing");
    }

    private static void MoreCodeToRun(object sender, System.EventArgs e)
    {
        System.Console.WriteLine("MoreCodeToRun is executing");
    }

    static void Main()
    {
        EventClass ec = new EventClass();

        ec.CustomEvent += new EventClass.CustomEventHandler(CodeToRun);
        ec.CustomEvent += new EventClass.CustomEventHandler(MoreCodeToRun); 

        System.Console.WriteLine("First Invocation:");
        ec.InvokeEvent();

        ec.CustomEvent -= new EventClass.CustomEventHandler(MoreCodeToRun);

        System.Console.WriteLine("\nSecond Invocation:");
        ec.InvokeEvent();
    }
}

Output

First Invocation:

CodeToRun is executing

MoreCodeToRun is executing

Second Invocation:

CodeToRun is executing

See Also

Tasks

Delegates Sample

Concepts

C# Programming Guide

Reference

Delegates (C# Programming Guide)

Events (C# Programming Guide)

Other Resources

The C# Programming Language for Java Developers