Other Generic Framework Types


In addition to the System.Collections.Generic namespace, the .NET Framework has other uses for generic types. The structs and delegates discussed here are all in the System namespace and serve different purposes.

This section discusses the following:

  • The struct Nullable<T>

  • The delegate EventHandler<TEventArgs>

  • The struct ArraySegment<T>

Nullable<T>

A number in the database and a number in a programming language have an important different characteristic insofar as the number of the database can be null. A number in C# cannot be null. Int32 is a struct, and because structs are implemented as value types, it cannot be null.

The problem doesn't exist only with databases but also with mapping XML data to .NET types.

This difference often causes headaches and lot of additional work to map the data. One solution could be to map numbers from databases and XML files to reference types, because reference types can have a null value. However, this also means additional overhead during runtime.

With the structure Nullable<T> this can be easily solved. In the example, Nullable<T> is instantiated with Nullable<int>. The variable x can now be used like an int, assigning values and using operators to do some calculation. This behavior is made possible by casting operators of the Nullable<T> type. However, x can also be null. The Nullable<T> properties HasValue and Value can check if there is a value, and the value can be accessed:

 Nullable<int> x; x = 4; x += 3; if (x.HasValue) { int y = x.Value; } x = null; 

Because nullable types are used very often, C# has a special syntax for defining variables of this type. Instead of using the syntax with the generic structure, the ? operator can be used. In the following example, the variables x1 and x2 both are instances of a nullable int type:

 Nullable<int> x1;  int? x2; 

A nullable type can be compared with null and numbers as shown. Here the value of x is compared with null, and if it is not null, it is compared with a value smaller than 0:

 int? x = GetNullableType(); if (x == null) Console.WriteLine("x is null"); else if (x < 0) Console.WriteLine("x is smaller than 0"); 

Nullable types can also be used with arithmetic operators. The variable x3 is the sum of the variables x1 and x2. If any of the nullable types has a null value, the result is null:

 int? x1 = GetNullableType(); int? x2 = GetNullableType(); int? x3 = x1 + x2; 

Non-nullable types can be converted to nullable types. With the conversion from a non-nullable type to a nullable type, an implicit conversion is possible where casting is not required. This conversion always succeeds:

 int y1 = 4;  int? x1 = y1; 

The other way around, the conversion from a nullable type to a non-nullable type, can fail. If the nullable type has a null value and the null value is assigned to a non-nullable type, an exception of type InvalidOperationException is thrown. That's the reason the cast operator is required to do an explicit conversion:

 int? x1 = GetNullableType();  int y1 = (int)x1; 

Instead of doing an explicit cast, it is also possible to convert a nullable type to a non-nullable type with the coalescing operator. The coalescing operator has the syntax ?? to define a default value for the conversion in case the nullable type has a value of null. Here, y1 gets the value 0 if x1 is null:

int? x1 = GetNullableType();  int y1 = x1 ?? 0; 

EventHandler<TEventArgs>

With Windows Forms and Web applications, delegates for many different event handlers are defined. Some of the event handlers are listed here:

 public sealed delegate void EventHandler(object sender, EventArgs e); public sealed delegate void PaintEventHandler(object sender, PaintEventArgs e); public sealed delegate void MouseEventHandler(object sender, MouseEventArgs e); 

These delegates have in common that the first argument is always the sender, who was the origin of the event, and the second argument is of a type to contain information specific to the event.

With the new EventHandler<TEventArgs>, it is not necessary to define a new delegate for every event handler. As you can see, the first parameter is defined the same way as before, but the second parameter is a generic type TEventArgs. The where clause defines that the type for TEventArgs must derive from the base class EventArgs:

 public sealed delegate void EventHandler<TEventArgs>(object sender, TEventArgs e) where TEventArgs : EventArgs 

ArraySegment<T>

The struct ArraySegment<T> represents a segment of an array. If parts of an array are needed, a segment can be used. With the struct ArraySegment<T>, the information about the segment (the offset and count) is contained within this structure.

In the example the variable arr is defined as an int array with 8 elements. The variable segment of type ArraySegment<int> is used to represent a segment of the integer array. The segment is initialized with the constructor where the array is passed together with an offset and an item count. Here the offset is set to 2, so you start with the third element, and the count is set to 3, so 6 is the last element of the segment.

The array behind the array segment can be accessed with the Array property. ArraySegment<T> also has the properties Offset and Count that indicate the initialized values to define the segment. The for loop is used to iterate through the array segment. The first expression of the for loop is initialized to the offset where the iteration should begin. With the second expression the count of the element numbers in the segment is used to check if the iteration should stop. Within the for loop the elements contained by the segment are accessed with the Array property:

 int[] arr = {1, 2, 3, 4, 5, 6, 7, 8}; ArraySegment<int> segment = new ArraySegment<int>(arr, 2, 3); for (int i = segment.Offset; i < segment.Offset + segment.Count; i++) { Console.WriteLine(segment.Array[i]); } 

With the example so far, you might question the usefulness of the ArraySegment<T> structure. However, the ArraySegment<T> can also be passed as an argument to methods. This way, just a single argument is needed instead of three that define the offset and count in addition to the array.

The method WorkWithSegment() gets an ArraySegment<string> as a parameter. In the implementation of this method, the properties Offset, Count, and Array are used as before:

 void WorkWithSegment(ArraySegment<string> segment) { for (int i = segment.Offset; i < segment.Offset + segment.Count; i++) { Console.WriteLine(segment.Array[i]); } } 



Professional C# 2005
Pro Visual C++ 2005 for C# Developers
ISBN: 1590596080
EAN: 2147483647
Year: 2005
Pages: 351
Authors: Dean C. Wills

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net