
Pro CSharp 2008 And The .NET 3.5 Platform [eng]
.pdf
402 CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS
static void Main(string[] args)
{
...
// Attempt to make an implicit cast?
Square s3; s3.Length = 83;
Rectangle rect2 = s3; Console.ReadLine();
}
This code will not compile, given that you have not provided an implicit conversion routine for the Rectangle type. Now here is the catch: it is illegal to define explicit and implicit conversion functions on the same type, if they do not differ by their return type or parameter set. This might seem like a limitation; however, the second catch is that when a type defines an implicit conversion routine, it is legal for the caller to make use of the explicit cast syntax!
Confused? To clear things up, let’s add an implicit conversion routine to the Rectangle structure using the C# implicit keyword (note that the following code assumes the width of the resulting Rectangle is computed by multiplying the side of the Square by 2):
public struct Rectangle
{
...
public static implicit operator Rectangle(Square s)
{
Rectangle r; r.Height = s.Length;
//Assume the length of the new Rectangle with
//(Length x 2)
r.Width = s.Length * 2; return r;
}
}
With this update, you are now able to convert between types as follows:
static void Main(string[] args)
{
...
//Implicit cast OK!
Square s3; s3.Length= 7; Rectangle rect2 = s3;
Console.WriteLine("rect2 = {0}", rect2); DrawSquare(s3);
//Explicit cast syntax still OK!
Square s4; s4.Length = 3;
Rectangle rect3 = (Rectangle)s4; Console.WriteLine("rect3 = {0}", rect3); Console.ReadLine();
}
Again, be aware that it is permissible to define explicit and implicit conversion routines for the same type as long as their signatures differ. Thus, you could update the Square as follows:

CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS |
403 |
public struct Square
{
...
//Can call as:
//Square sq2 = (Square)90;
//or as:
//Square sq2 = 90;
public static implicit operator Square(int sideLength)
{
Square newSq; newSq.Length = sideLength; return newSq;
}
//Must call as:
//int side = (int)mySquare;
public static explicit operator int (Square s) { return s.Length; }
}
The Internal Representation of Custom Conversion Routines
Like overloaded operators, methods that are qualified with the implicit or explicit keywords have “special” names in terms of CIL: op_Implicit and op_Explicit, respectively (see Figure 12-6).
Figure 12-6. CIL representation of user-defined conversion routines
■Note The Visual Studio 2008 Object Browser shows custom conversion operators using the “explicit operator” and “implicit operator” icons.
That wraps up our examination of defining custom conversion routines. As with overloaded operators, remember that this bit of syntax is simply a shorthand notation for “normal” member

404CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS
functions, and in this light it is always optional. When used correctly, however, your custom structures can be used more naturally, as they can be treated as true class types related by inheritance.
■Source Code The CustomConversions project is located under the Chapter 12 subdirectory.
Working with Pointer Types
In Chapter 4, you learned that the .NET platform defines two major categories of data: value types and reference types. Truth be told, however, there is a third category: pointer types. To work with pointer types, we are provided with specific operators and keywords that allow us to bypass the CLR’s memory management scheme and take matters into our own hands (see Table 12-3).
Table 12-3. Pointer-Centric C# Operators and Keywords
Operator/Keyword |
Meaning in Life |
* |
This operator is used to create a pointer variable (i.e., a variable that |
|
represents a direct location in memory). As in C(++), this same operator is |
|
used for pointer indirection. |
& |
This operator is used to obtain the address of a variable in memory. |
-> |
This operator is used to access fields of a type that is represented by a pointer |
|
(the unsafe version of the C# dot operator). |
[] |
The [] operator (in an unsafe context) allows you to index the slot pointed to |
|
by a pointer variable (recall the interplay between a pointer variable and the |
|
[] operator in C(++)!). |
++, -- |
In an unsafe context, the increment and decrement operators can be applied |
|
to pointer types. |
+, - |
In an unsafe context, the addition and subtraction operators can be applied |
|
to pointer types. |
==, !=, <, >, <=, => |
In an unsafe context, the comparison and equality operators can be applied |
|
to pointer types. |
stackalloc |
In an unsafe context, the stackalloc keyword can be used to allocate C# |
|
arrays directly on the stack. |
fixed |
In an unsafe context, the fixed keyword can be used to temporarily fix a |
|
variable so that its address may be found. |
|
|
Now, before we dig into the details, let me point out the fact that you will seldom if ever need to make use of pointer types. Although C# does allow you to drop down to the level of pointer manipulations, understand that the .NET runtime has absolutely no clue of your intentions. Thus, if you mismanage a pointer, you are the one in charge of dealing with the consequences. Given these warnings, when exactly would you need to work with pointer types? There are two common situations:
•You are looking to optimize select parts of your application by directly manipulating memory outside the management of the CLR.
•You are calling methods of a C-based *.dll or COM server that demand pointer types as parameters. Even in this case, you can often bypass the use of pointer types in favor of the
System.IntPtr type and members of the System.Runtime.InteropServices.Marshal type.

CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS |
405 |
In the event that you do decide to make use of this C# language feature, you will be required to inform the C# compiler (csc.exe) of your intentions by enabling your project to support “unsafe code.” To do so at the command line, simply supply the /unsafe flag as an argument:
csc /unsafe *.cs
From Visual Studio 2008, you will need to access your project’s Properties page and check the Allow Unsafe Code check box from the Build tab (see Figure 12-7). To experiment with pointer types, create a new Console Application project named UnsafeCode and enable unsafe code.
Figure 12-7. Enabling unsafe code using Visual Studio 2008
■Note In the examples that follow, I’m assuming that you have some background in C(++) pointer manipulations. If this is not true in your case, feel free to skip this topic entirely. Again, writing unsafe code will not be a common task for a vast majority of C# applications.
The unsafe Keyword
When you wish to work with pointers in C#, you must specifically declare a block of “unsafe code” using the unsafe keyword (any code that is not marked with the unsafe keyword is considered “safe” automatically). For example, the following Program class declares a scope of unsafe code within the safe Main() method:
class Program
{
static void Main(string[] args)
{
unsafe
{
// Work with pointer types here!
}
// Can't work with pointers here!
}
}

406 CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS
In addition to declaring a scope of unsafe code within a method, you are able to build structures, classes, type members, and parameters that are “unsafe.” Here are a few examples to gnaw on (no need to define these types in your current project):
//This entire structure is "unsafe" and can
//be used only in an unsafe context. public unsafe struct Node
{
public int Value; public Node* Left; public Node* Right;
}
//This struct is safe, but the Node2* members
//are not. Technically, you may access "Value" from
//outside an unsafe context, but not "Left" and "Right". public struct Node2
{
public int Value;
// These can be accessed only in an unsafe context! public unsafe Node2* Left;
public unsafe Node2* Right;
}
Methods (static or instance level) may be marked as unsafe as well. For example, assume that you know a particular static method will make use of pointer logic. To ensure that this method can be called only from an unsafe context, you could define the method as follows:
unsafe static void SquareIntPointer(int* myIntPointer)
{
// Square the value just for a test.
*myIntPointer *= *myIntPointer;
}
The configuration of our method demands that the caller invoke SquareIntPointer() as follows:
static void Main(string[] args)
{
unsafe
{
int myInt = 10;
// OK, because we are in an unsafe context.
SquareIntPointer(&myInt); Console.WriteLine("myInt: {0}", myInt);
}
int myInt2 = 5;
// Compiler error! Must be in unsafe context!
SquareIntPointer(&myInt2); Console.WriteLine("myInt: {0}", myInt2);
}
If you would rather not force the caller to wrap the invocation within an unsafe context, you could update Main() with the unsafe keyword. In this case, the following code would compile:

CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS |
407 |
unsafe static void Main(string[] args)
{
int myInt2 = 5; SquareIntPointer(&myInt2); Console.WriteLine("myInt: {0}", myInt2);
}
Working with the * and & Operators
Once you have established an unsafe context, you are then free to build pointers to data types using the * operator and obtain the address of said pointer using the & operator. Unlike in C or C++, using C#, the * operator is applied to the underlying type only, not as a prefix to each pointer variable name. For example, consider the following code, which illustrates the correct and incorrect way to declare pointers to integer variables:
//No! This is incorrect under C#! int *pi, *pj;
//Yes! This is the way of C#. int* pi, pj;
Consider the following unsafe method:
unsafe static void PrintValueAndAddress()
{
int myInt;
//Define an int pointer, and
//assign it the address of myInt. int* ptrToMyInt = &myInt;
//Assign value of myInt using pointer indirection.
*ptrToMyInt = 123;
//Print some stats.
Console.WriteLine("Value of myInt {0}", myInt); Console.WriteLine("Address of myInt {0:X}", (int)&ptrToMyInt);
}
An Unsafe (and Safe) Swap Function
Of course, declaring pointers to local variables simply to assign their value (as shown in the previous example) is never required and not altogether useful. To illustrate a more practical example of unsafe code, assume you wish to build a swap function using pointer arithmetic:
unsafe public static void UnsafeSwap(int* i, int* j)
{
int temp = *i; *i = *j;
*j = temp;
}
Very C-like, don’t you think? However, given your work in Chapter 4 you should be aware that you could write the following safe version of your swap algorithm using the C# ref keyword:

408CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS
public static void SafeSwap(ref int i, ref int j)
{
int temp = i;
i= j;
j= temp;
}
The functionality of each method is identical, thus reinforcing the point that direct pointer manipulation is not a mandatory task under C#. Here is the calling logic using a safe Main(), with an unsafe context:
static void Main(string[] args)
{
Console.WriteLine("***** Calling method with unsafe code *****");
//Values for swap. int i = 10, j = 20;
//Swap values "safely."
Console.WriteLine("\n***** Safe swap *****");
Console.WriteLine("Values before safe swap: i = {0}, j = {1}", i, j); SafeSwap(ref i, ref j);
Console.WriteLine("Values after safe swap: i = {0}, j = {1}", i, j);
//Swap values "unsafely."
Console.WriteLine("\n***** Unsafe swap *****");
Console.WriteLine("Values before unsafe swap: i = {0}, j = {1}", i, j); unsafe { UnsafeSwap(&i, &j); }
Console.WriteLine("Values after unsafe swap: i = {0}, j = {1}", i, j); Console.ReadLine();
}
Field Access via Pointers (the -> Operator)
Now assume that you have defined a simple safe Point structure as follows:
struct Point
{
public int x; public int y;
public override string ToString()
{ return string.Format("({0}, {1})", x, y);}
}
If you declare a pointer to a Point type, you will need to make use of the pointer-field access operator (represented by ->) to access its public members. As shown in Table 12-3, this is the unsafe version of the standard (safe) dot operator (.). In fact, using the pointer indirection operator (*), it is possible to dereference a pointer to (once again) apply the dot operator notation. Check out the unsafe method:
unsafe static void UsePointerToPoint()
{
// Access members via pointer.
Point point; Point* p = &point; p->x = 100;
p->y = 200; Console.WriteLine(p->ToString());

CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS |
409 |
// Access members via pointer indirection.
Point point2; Point* p2 = &point2; (*p2).x = 100; (*p2).y = 200;
Console.WriteLine((*p2).ToString());
}
The stackalloc Keyword
In an unsafe context, you may need to declare a local variable that allocates memory directly from the call stack (and is therefore not subject to .NET garbage collection). To do so, C# provides the stackalloc keyword, which is the C# equivalent to the _alloca function of the C runtime library. Here is a simple example:
unsafe static void UnsafeStackAlloc()
{
char* p = stackalloc char[256]; for (int k = 0; k < 256; k++)
p[k] = (char)k;
}
Pinning a Type via the fixed Keyword
As you saw in the previous example, allocating a chunk of memory within an unsafe context may be facilitated via the stackalloc keyword. By the very nature of this operation, the allocated memory is cleaned up as soon as the allocating method has returned (as the memory is acquired from the stack). However, assume a more complex example. During our examination of the -> operator, you created a value type named Point. Like all value types, the allocated memory is popped off the stack once the executing scope has terminated. For the sake of argument, assume Point was instead defined as a reference type:
class PointRef // <= Renamed and retyped.
{
public int x; public int y;
public override string ToString()
{ return string.Format("({0}, {1})", x, y);}
}
As you are well aware, if the caller declares a variable of type Point, the memory is allocated on the garbage-collected heap. The burning question then becomes, “What if an unsafe context wishes to interact with this object (or any object on the heap)?” Given that garbage collection can occur at any moment, imagine the problems encountered when accessing the members of Point at the very point in time at which a sweep of the heap is under way. Theoretically, it is possible that the unsafe context is attempting to interact with a member that is no longer accessible or has been repositioned on the heap after surviving a generational sweep (which is an obvious problem).
To lock a reference type variable in memory from an unsafe context, C# provides the fixed keyword. The fixed statement sets a pointer to a managed type and “pins” that variable during the execution of statement. Without fixed, pointers to managed variables would be of little use, since garbage collection could relocate the variables unpredictably. (In fact, the C# compiler will not allow you to set a pointer to a managed variable except in a fixed statement.)

410 CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS
Thus, if you create a Point type (now redesigned as a class) and want to interact with its members, you must write the following code (or receive a compiler error):
unsafe public static void UseAndPinPoint()
{
PointRef pt = new PointRef (); pt.x = 5;
pt.y = 6;
//pin pt in place so it will not
//be moved or GC-ed.
fixed (int* p = &pt.x)
{
// Use int* variable here!
}
// pt is now unpinned, and ready to be GC-ed.
Console.WriteLine ("Point is: {0}", pt);
}
In a nutshell, the fixed keyword allows you to build a statement that locks a reference variable in memory, such that its address remains constant for the duration of the statement. To be sure, any time you interact with a reference type from within the context of unsafe code, pinning the reference is a must.
The sizeof Keyword
The final unsafe-centric C# keyword to consider is sizeof. As in C(++), the C# sizeof keyword is used to obtain the size in bytes for a value type (never a reference type), and it may only be used within an unsafe context. As you may imagine, this ability may prove helpful when you’re interacting with unmanaged C-based APIs. Its usage is straightforward:
unsafe static void UseSizeOfOperator()
{
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 long is {0}.", sizeof(long));
}
As sizeof will evaluate the number of bytes for any System.ValueType-derived entity, you are able to obtain the size of custom structures as well. For example, we could pass the Point structure into sizeof as follows:
unsafe static void UseSizeOfOperator()
{
...
Console.WriteLine("The size of Point is {0}.", sizeof(Point));
}
■Source Code The UnsafeCode project can be found under the Chapter 12 subdirectory.

CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS |
411 |
C# Preprocessor Directives
Like many other languages in the C family, C# supports the use of various symbols that allow you to interact with the compilation process. Before examining various C# preprocessor directives, let’s get our terminology correct. The term “C# preprocessor directive” is not entirely accurate. In reality, this term is used only for consistency with the C and C++ programming languages. In C#, there is no separate preprocessing step. Rather, preprocessing directives are processed as part of the lexical analysis phase of the compiler.
In any case, the syntax of the C# preprocessor directives is very similar to that of the other members of the C family, in that the directives are always prefixed with the pound sign (#). Table 12-4 defines some of the more commonly used directives (consult the .NET Framework 3.5 SDK documentation for complete details).
Table 12-4. Common C# Preprocessor Directives
Directives |
Meaning in Life |
#region, #endregion |
Used to mark sections of collapsible source code |
#define, #undef |
Used to define and undefine conditional compilation symbols |
#if, #elif, #else, #endif |
Used to conditionally skip sections of source code (based on specified |
|
compilation symbols) |
|
|
Specifying Code Regions
Perhaps some of the most useful of all preprocessor directives are #region and #endregion. Using these tags, you are able to specify a block of code that may be hidden from view and identified by a friendly textual marker. Use of regions can help keep lengthy *.cs files more manageable. For example, you could create one region for a type’s constructors, another for type properties, and so forth:
class Car
{
private string petName; private int currSp;
#region Constructors public Car()
{ ... }
public Car (int currSp, string petName) { ... }
#endregion
#region Properties public int Speed { ... }
public string Name { ... }
#endregion
}
When you place your mouse cursor over a collapsed region, you are provided with a snapshot of the code lurking behind (see Figure 12-8).