Events in C#

Events

Events provide a way for a class or object to notify other classes or objects when something of interest happens. The class that sends (or raises) the event is called the publisher and the classes that receive (or handle) the event are called subscribers.

In a typical C# Windows Forms or Web application, you subscribe to events raised by controls such as buttons and list boxes. You can use the Visual C# integrated development environment (IDE) to browse the events that a control publishes and select the ones that you want to handle. The IDE automatically adds an empty event handler method and the code to subscribe to the event. For more information, see How to: Subscribe to and Unsubscribe from Events (C# Programming Guide).

Events Overview

Events have the following properties:

  • The publisher determines when an event is raised; the subscribers determine what action is taken in response to the event.

  • An event can have multiple subscribers. A subscriber can handle multiple events from multiple publishers.

  • Events that have no subscribers are never called.

  • Events are commonly used to signal user actions such as button clicks or menu selections in graphical user interfaces.

  • When an event has multiple subscribers, the event handlers are invoked synchronously when an event is raised. To invoke events asynchronously, see Calling Synchronous Methods Asynchronously.

  • Events can be used to synchronize threads.

  • In the .NET Framework class library, events are based on the EventHandler delegate and the EventArgs base class.

Delegates In C#

Delegates

A delegate is a type that references a method. Once a delegate is assigned a method, it behaves exactly like that method. The delegate method can be used like any other method, with parameters and a return value, as in this example:

C#
public delegate int PerformCalculation(int x, int y);

Any method that matches the delegate's signature, which consists of the return type and parameters, can be assigned to the delegate. This makes is possible to programmatically change method calls, and also plug new code into existing classes. As long as you know the delegate's signature, you can assign your own delegated method.

This ability to refer to a method as a parameter makes delegates ideal for defining callback methods. For example, a sort algorithm could be passed a reference to the method that compares two objects. Separating the comparison code allows the algorithm to be written in a more general way.

Delegates Overview

Delegates have the following properties:

  • Delegates are similar to C++ function pointers, but are type safe.

  • Delegates allow methods to be passed as parameters.

  • Delegates can be used to define callback methods.

  • Delegates can be chained together; for example, multiple methods can be called on a single event.

  • Methods don't need to match the delegate signature exactly. For more information, see Covariance and Contravariance

  • C# version 2.0 introduces the concept of Anonymous Methods, which permit code blocks to be passed as parameters in place of a separately defined method.

Indexers in C#

Indexers

Indexers permit instances of a class or struct to be indexed in the same way as arrays. Indexers are similar to properties except that their accessors take parameters.

In the following example, a generic class is defined and provided with simple get and set accessor methods as a means for assigning and retrieving values. The class Program creates an instance of this class for storing strings.

C#
class SampleCollection
{
private T[] arr = new T[100];
public T this[int i]
{
get
{
return arr[i];
}
set
{
arr[i] = value;
}
}
}

// This class shows how client code uses the indexer
class Program
{
static void Main(string[] args)
{
SampleCollection<string> stringCollection = new SampleCollection<string>();
stringCollection[0] = "Hello, World";
System.Console.WriteLine(stringCollection[0]);
}
}
  • Indexers enable objects to be indexed in a similar way to arrays.

  • A get accessor returns a value. A set accessor assigns a value.

  • The this keyword is used to define the indexers.

  • The value keyword is used to define the value being assigned by the set indexer.

  • Indexers do not have to be indexed by an integer value; it is up to you how to define the specific look-up mechanism.

  • Indexers can be overloaded.

  • Indexers can have more than one formal parameter, for example, when accessing a two-dimensional array.

Properties In C#

Properties

Properties are members that provide a flexible mechanism to read, write, or compute the values of private fields. Properties can be used as though they are public data members, but they are actually special methods called accessors. This enables data to be accessed easily while still providing the safety and flexibility of methods.

In this example, the class TimePeriod stores a time period. Internally the class stores the time in seconds, but a property called Hours is provided that allows a client to specify a time in hours. The accessors for the Hours property perform the conversion between hours and seconds.

C#

class TimePeriod
{
private double seconds;

public double Hours
{
get { return seconds / 3600; }
set { seconds = value * 3600; }
}
}

class Program
{
static void Main()
{
TimePeriod t = new TimePeriod();

// Assigning the Hours property causes the 'set' accessor to be called.
t.Hours = 24;

// Evaluating the Hours property causes the 'get' accessor to be called.
System.Console.WriteLine("Time in hours: " + t.Hours);
}
}

Time in hours: 24

  • Properties enable a class to expose a public way of getting and setting values, while hiding implementation or verification code.

  • A get property accessor is used to return the property value, and a set accessor is used to assign a new value. These accessors can have different access levels. For more information, see Accessor Accessibility.

  • The value keyword is used to define the value being assigned by the set indexer.

  • Properties that do not implement a set method are read only.

C# TIPS

How to: Know the Difference Between Passing a Struct and Passing a Class Reference to a Method (C# Programming Guide)

This example shows that when a struct is passed to a method, a copy of the struct is passed, but when a class instance is passed, a reference is passed.

The output of the following example shows that only the value of the class field is changed when the class instance is passed to the ClassTaker method. The struct field, however, does not change by passing its instance to the StructTaker method. This is because a copy of the struct is passed to the StructTaker method, while a reference to the class is passed to the ClassTaker method.

Example

C#

class TheClass
{
public string willIChange;
}

struct TheStruct
{
public string willIChange;
}

class TestClassAndStruct
{
static void ClassTaker(TheClass c)
{
c.willIChange = "Changed";
}

static void StructTaker(TheStruct s)
{
s.willIChange = "Changed";
}

static void Main()
{
TheClass testClass = new TheClass();
TheStruct testStruct = new TheStruct();

testClass.willIChange = "Not Changed";
testStruct.willIChange = "Not Changed";

ClassTaker(testClass);
StructTaker(testStruct);

System.Console.WriteLine("Class field = {0}", testClass.willIChange);
System.Console.WriteLine("Struct field = {0}", testStruct.willIChange);
}
}

Output

Class field = Changed
Struct field = Not Changed

Static Classes and Static Class Members in C#

Static Classes and Static Class Members

Static classes and class members are used to create data and functions that can be accessed without creating an instance of the class. Static class members can be used to separate data and behavior that is independent of any object identity: the data and functions do not change regardless of what happens to the object. Static classes can be used when there is no data or behavior in the class that depends on object identity.

A class can be declared static, indicating that it contains only static members. It is not possible to create instances of a static class using the new keyword. Static classes are loaded automatically by the .NET Framework common language runtime (CLR) when the program or namespace containing the class is loaded.

Use a static class to contain methods that are not associated with a particular object. For example, it is a common requirement to create a set of methods that do not act on instance data and are not associated to a specific object in your code. You could use a static class to hold those methods.

The main features of a static class are:

Creating a static class is therefore much the same as creating a class that contains only static members and a private constructor. A private constructor prevents the class from being instantiated.

The advantage of using a static class is that the compiler can check to make sure that no instance members are accidentally added. The compiler will guarantee that instances of this class cannot be created.

Static classes are sealed and therefore cannot be inherited. Static classes cannot contain a constructor, although it is still possible to declare a static constructor to assign initial values or set up some static state. For more information, see Static Constructors (C# Programming Guide).

Suppose you have a class CompanyInfo that contains the following methods to get information about the company name and address.

C#

class CompanyInfo
{
public string GetCompanyName() { return "CompanyName"; }
public string GetCompanyAddress() { return "CompanyAddress"; }
//...
}

These methods do not need to be attached to a specific instance of the class. Therefore, instead of creating unnecessary instances of this class, you can declare it as a static class, like this:

C#

static class CompanyInfo
{
public static string GetCompanyName() { return "CompanyName"; }
public static string GetCompanyAddress() { return "CompanyAddress"; }
//...
}

Use a static class as a unit of organization for methods not associated with particular objects. Also, a static class can make your implementation simpler and faster because you do not have to create an object in order to call its methods. It is useful to organize the methods inside the class in a meaningful way, such as the methods of the Math class in the System namespace.

A static method, field, property, or event is callable on a class even when no instance of the class has been created. If any instances of the class are created, they cannot be used to access the static member. Only one copy of static fields and events exists, and static methods and properties can only access static fields and static events. Static members are often used to represent data or calculations that do not change in response to object state; for instance, a math library might contain static methods for calculating sine and cosine.

Static class members are declared using the static keyword before the return type of the member, for example:

C#

public class Automobile
{
public static int NumberOfWheels = 4;
public static int SizeOfGasTank
{
get
{
return 15;
}
}
public static void Drive() { }
public static event EventType RunOutOfGas;

//other non-static fields and properties...
}

Static members are initialized before the static member is accessed for the first time, and before the static constructor, if any is called. To access a static class member, use the name of the class instead of a variable name to specify the location of the member. For example:

C#

Automobile.Drive();
int i = Automobile.NumberOfWheels;

Here is an example of a static class that contains two methods that convert temperature from Celsius to Fahrenheit and vice versa:

public static class TemperatureConverter
{
public static double CelsiusToFahrenheit(string temperatureCelsius)
{
// Convert argument to double for calculations.
double celsius = System.Double.Parse(temperatureCelsius);

// Convert Celsius to Fahrenheit.
double fahrenheit = (celsius * 9 / 5) + 32;

return fahrenheit;
}

public static double FahrenheitToCelsius(string temperatureFahrenheit)
{
// Convert argument to double for calculations.
double fahrenheit = System.Double.Parse(temperatureFahrenheit);

// Convert Fahrenheit to Celsius.
double celsius = (fahrenheit - 32) * 5 / 9;

return celsius;
}
}

class TestTemperatureConverter
{
static void Main()
{
System.Console.WriteLine("Please select the convertor direction");
System.Console.WriteLine("1. From Celsius to Fahrenheit.");
System.Console.WriteLine("2. From Fahrenheit to Celsius.");
System.Console.Write(":");

string selection = System.Console.ReadLine();
double F, C = 0;

switch (selection)
{
case "1":
System.Console.Write("Please enter the Celsius temperature: ");
F = TemperatureConverter.CelsiusToFahrenheit(System.Console.ReadLine());
System.Console.WriteLine("Temperature in Fahrenheit: {0:F2}", F);
break;

case "2":
System.Console.Write("Please enter the Fahrenheit temperature: ");
C = TemperatureConverter.FahrenheitToCelsius(System.Console.ReadLine());
System.Console.WriteLine("Temperature in Celsius: {0:F2}", C);
break;

default:
System.Console.WriteLine("Please select a convertor.");
break;
}
}
}

2

98.6

Please select the convertor

1. From Celsius to Fahrenheit.

2. From Fahrenheit to Celsius.

:2

Please enter the Fahrenheit temperature: 98.6

Temperature in Celsius: 37.00

Additional sample output might look as follows:

Please select the convertor

1. From Celsius to Fahrenheit.

2. From Fahrenheit to Celsius.

:1

Please enter the Celsius temperature: 37.00

Temperature in Fahrenheit: 98.60

Partial Class Definitions In C#

Partial Class Definitions

It is possible to split the definition of a class or a struct, or an interface over two or more source files. Each source file contains a section of the class definition, and all parts are combined when the application is compiled. There are several situations when splitting a class definition is desirable:

  • When working on large projects, spreading a class over separate files allows multiple programmers to work on it simultaneously.

  • When working with automatically generated source, code can be added to the class without having to recreate the source file. Visual Studio uses this approach when creating Windows Forms, Web Service wrapper code, and so on. You can create code that uses these classes without having to edit the file created by Visual Studio.

  • To split a class definition, use the partial keyword modifier, as shown below:

C#
public partial class Employee
{
public void DoWork()
{
}
}

public partial class Employee
{
public void GoToLunch()
{
}
}

Using the partial keyword indicates that other parts of the class, struct, or interface can be defined within the namespace. All the parts must use the partial keyword. All of the parts must be available at compile time to form the final type. All the parts must have the same accessibility, such as public, private, and so on.

If any of the parts are declared abstract, then the entire type is considered abstract. If any of the parts are declared sealed, then the entire type is considered sealed. If any of the parts declare a base type, then the entire type inherits that class.

All of the parts that specify a base class must agree, but parts that omit a base class still inherit the base type. Parts can specify different base interfaces, and the final type implements all of the interfaces listed by all of the partial declarations. Any class, struct, or interface members declared in a partial definition are available to all of the other parts. The final type is the combination of all the parts at compile time.

NoteNote

The partial modifier is not available on delegate or enumeration declarations.

Nested types can be partial, even if the type they are nested within is not partial itself. For example:

C#

class Container
{
partial class Nested
{
void Test() { }
}
partial class Nested
{
void Test2() { }
}
}
  • At compile time, attributes of partial-type definitions are merged. For example, the following declarations:

C#

[System.SerializableAttribute]
partial class Moon { }

[System.ObsoleteAttribute]
partial class Moon { }

are equivalent to:

C#

[System.SerializableAttribute]
[System.ObsoleteAttribute]
class Moon { }
  • The following are merged together from all the partial-type definitions:

  • XML comments

  • interfaces

  • generic-type parameter attributes

  • class attributes

  • members

For example, the following declarations:

C#

partial class Earth : Planet, IRotate { }
partial class Earth : IRevolve { }

are equivalent to:

C#

class Earth : Planet, IRotate, IRevolve { }

There are several rules to follow when working with partial class definitions:

  • All partial-type definitions meant to be parts of the same type must be modified with partial. For example, the following class declarations generate an error:

    C#

    public partial class A { }
    //public class A { } // Error, must also be marked partial
  • The partial modifier can only appear immediately before the keywords class, struct, or interface.

  • Nested partial types are allowed in partial-type definitions, for example:

    C#

    partial class ClassWithNestedClass
    {
    partial class NestedClass { }
    }

    partial class ClassWithNestedClass
    {
    partial class NestedClass { }
    }
  • All partial-type definitions meant to be parts of the same type must be defined in the same assembly and the same module (.exe or .dll file). Partial definitions cannot span multiple modules.

  • The class name and generic-type parameters must match on all partial-type definitions. Generic types can be partial. Each partial declaration must use the same parameter names in the same order.

  • The following keywords on a partial-type definition are optional, but if present on one partial-type definition, cannot conflict with the keywords specified on another partial definition for the same type:

In the following example, the fields and the constructor of the class, CoOrds, are declared in one partial class definition, while the member, PrintCoOrds, is declared in another partial class definition.

C#

public partial class CoOrds
{
private int x;
private int y;

public CoOrds(int x, int y)
{
this.x = x;
this.y = y;
}
}

public partial class CoOrds
{
public void PrintCoOrds()
{
System.Console.WriteLine("CoOrds: {0},{1}", x, y);
}

}

class TestCoOrds
{
static void Main()
{
CoOrds myCoOrds = new CoOrds(10, 15);
myCoOrds.PrintCoOrds();
}
}

CoOrds: 10,15

The following example shows that you can also develop partial structs and interfaces.

C#

partial interface ITest
{
void Interface_Test();
}

partial interface ITest
{
void Interface_Test2();
}

partial struct S1
{
void Struct_Test() { }
}

partial struct S1
{
void Struct_Test2() { }
}