Nullable<T>
A number in a database and a number in a
programming language have an important difference in their
characteristics, as a number in the database can be null. A number in C# cannot be null. Int32 is a struct,
and because structs are implemented as value types, they 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 is 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 resolved. 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:
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:
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:
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:
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:
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:
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:
EventHandler<TEventArgs>
With Windows Forms and Web applications,
delegates for many different event handlers are defined. Some of
the event handlers are listed here:
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 specifies that the type for TEventArgs must be derived from the base class
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 eight 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:
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:
|
|
Important |
It’s important to note that array segments
don’t copy the elements of the originating array. Instead, the
originating array can be accessed through ArraySegment<T>. If elements of the array
segment are changed, the changes can be seen in the original
array.
|