
Pro CSharp 2008 And The .NET 3.5 Platform [eng]
.pdf
412 CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS
Figure 12-8. Regions at work
Conditional Code Compilation
The next batch of preprocessor directives (#if, #elif, #else, #endif) allows you to conditionally compile a block of code, based on predefined symbols. The classic use of these directives is to identify a block of code that is compiled only under a debug (rather than a release) build:
class Program
{
static void Main(string[] args)
{
#region Print machine info under DEBUG build
//This code will only execute if the project is
//compiled as a debug build.
#if DEBUG
Console.WriteLine("App directory: {0}", Environment.CurrentDirectory);
Console.WriteLine("Box: {0}", Environment.MachineName); Console.WriteLine("OS: {0}", Environment.OSVersion);
Console.WriteLine(".NET Version: {0}", Environment.Version);
#endif
#endregion
}
}

CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS |
413 |
Here, you are checking for a symbol named DEBUG. If it is present, you dump out a number of interesting statistics using some static members of the System.Environment class. If the DEBUG symbol is not defined, the code placed between #if and #endif will not be compiled into the resulting assembly, and it will be effectively ignored.
■Note The System.Diagnostics namespace provides the [Conditional] attribute, which can be applied to a class or method. Chapter 16 will explain the role of attributes in detail; however, for now, simply know that if you use [Conditional], you are not required to use the related preprocessor symbols.
By default, Visual Studio 2008 always defines a DEBUG symbol; however, this can be prevented by deselecting the Define DEBUG constant check box option located under the Build tab of your project’s Properties page. Assuming you did disable this autogenerated DEBUG symbol, you could now define this symbol on a file-by-file basis using the #define preprocessor directive:
#define DEBUG using System;
namespace PreprocessorDirectives
{
class Program
{
static void Main(string[] args)
{
// Same code as before...
}
}
}
■Note #define directives must be listed before anything else in a C# code file.
You are also able to define your own custom preprocessor symbols. For example, assume you have authored a C# class that should be compiled a bit differently under the Mono distribution of
.NET (see Appendix B). Using #define, you can define a symbol named MONO_BUILD on a file-by-file basis:
#define DEBUG #define MONO_BUILD
using System;
namespace PreprocessorDirectives
{
class Program
{
static void Main(string[] args)
{
#if MONO_BUILD
Console.WriteLine("Compiling under Mono!"); #else
Console.WriteLine("Compiling under Microsoft .NET");

414 CHAPTER 12 ■ INDEXERS, OPERATORS, AND POINTERS
#endif
}
}
}
To create a project-wide symbol, make use of the Conditional compilation symbols text box located on the Build tab of your project’s Properties page (see Figure 12-9).
Figure 12-9. Defining a projectwide preprocessor symbol
■Source Code The PreprocessorDirectives project can be found under the Chapter 12 subdirectory.
Summary
The purpose of this chapter is to deepen your understanding of the C# programming language. You began by investigating various advanced type construction techniques (indexer methods, overloaded operators, and custom conversion routines). You spent the remainder of this chapter examining a small set of lesser-known keywords (e.g., sizeof, checked, unsafe, and so forth), and during the process came to learn how to work with raw pointer types. As stated throughout the chapter’s examination of pointer types, a vast majority of your C# applications will never need to make use of them.
We wrapped up with an examination of the core C# preprocessor directives, which allow you to interact with the compiler (or in the case of #region/#endregion, Visual Studio 2008) regarding the compilation of your code files.


416 CHAPTER 13 ■ C# 2008 LANGUAGE FEATURES
var myInt = 0; var myBool = true;
var myString = "Time, marches on...";
}
■Note Strictly speaking, var is not a C# keyword. It is permissible to declare variables, parameters, and fields named “var” without compile-time errors. However, when the var token is used as a data type, it is contextually treated as a keyword by the compiler. For simplicity, I will use the term “var keyword,” rather than the more cumbersome “contextual var token.”
In this case, the compiler is able to infer that myInt is in fact a System.Int32, myBool is a System.Boolean, and myString is indeed of type System.String, given the initially assigned value. You can verify this by printing out the type name via reflection:
static void DeclareImplicitVars()
{
//Implicitly typed local variables. var myInt = 0;
var myBool = true;
var myString = "Time, marches on...";
//Print out the underlying type.
Console.WriteLine("myInt is a: {0}", myInt.GetType().Name); Console.WriteLine("myBool is a: {0}", myBool.GetType().Name); Console.WriteLine("myString is a: {0}", myString.GetType().Name);
}
Be aware that you can use this implicit typing for any type including arrays, generic types, and your own custom types:
static void DeclareImplicitVars()
{
...
// More implicitly typed local variables. var evenNumbers = new int[] { 2, 4, 6, 8 }; var myMinivans = new List<MiniVan>();
var myCar = new SportsCar();
Console.WriteLine("evenNumbers is a: {0}", evenNumbers.GetType().Name); Console.WriteLine("myMinivans is a: {0}", myMinivans.GetType().Name); Console.WriteLine("myCar is a: {0}", myCar.GetType().Name);
}
If you were to call the DeclareImplicitVars() method from within Main(), you’d find the output shown in Figure 13-1.

CHAPTER 13 ■ C# 2008 LANGUAGE FEATURES |
417 |
Figure 13-1. Reflecting over implicitly defined local variables
Use of var Within foreach Constructs
It is also possible to make use of implicit typing within a foreach looping construct. As you would expect, the compiler will correctly infer the correct “type of type.” Consider the following method, which iterates over an implicitly typed local array of integers:
static void VarInForeachLoop()
{
var evenNumbers = new int[] { 2, 4, 6, 8 };
// Use "var" in a standard foreach loop. foreach (var item in evenNumbers)
{
Console.WriteLine("Item value: {0}", item);
}
}
Understand, however, that a foreach loop can make use of a strongly typed iterator when processing an implicitly defined local array. Thus, the following code is also syntactically correct:
static void VarInForeachLoop()
{
var evenNumbers = new int[] { 2, 4, 6, 8 };
// Use a strongly typed System.Int32 to iterate over contents. foreach (int item in evenNumbers)
{
Console.WriteLine("Item value: {0}", item);
}
}
Restrictions on Implicitly Typed Variables
There are, of course, various restrictions regarding the use of the var keyword. First and foremost, implicit typing applies only to local variables in a method or property scope. It is illegal to use the var keyword to define return values, parameters, or field data of a type:
class ThisWillNeverCompile
{
// Error! var cannot be used as field data! private var myInt = 10;

418CHAPTER 13 ■ C# 2008 LANGUAGE FEATURES
//Error! var cannot be used as a return value
//or parameter type!
public var MyMethod(var x, var y){}
}
As well, local variables declared with the var keyword must be assigned an initial value at the exact time of declaration and cannot be assigned the initial value of null. The first restriction makes the act of defining an implicitly typed variable look and feel like the process of defining a constant data point with the const keyword (see Chapter 5). This last restriction should make sense, given that the compiler cannot infer what sort of type in memory the variable would be pointing to based only on null:
//Error! Must assign a value! var myData;
//Error! Must assign value at exact time of declaration! var myInt;
myInt = 0;
//Error! Can't assign null as initial value!
var myObj = null;
It is permissible, however, to assign an inferred local variable to null after its initial assignment (provided it is a reference type):
// OK, is SportsCar is a reference type! var myCar = new SportsCar();
myCar = null;
Furthermore, it is permissible to assign the value of an implicitly typed local variable to the value of other variables, implicitly typed or not:
// Also OK! var myInt = 0;
var anotherInt = myInt;
string myString = "Wake up!"; var myData = myString;
As well, it is permissible to return an implicitly typed local variable to the caller, provided that the method return type is the same underlying type as the var-defined data point:
static int GetAnInt()
{
var retVal = 9; return retVal;
}
Last but not least, be aware that it is illegal to define a nullable implicitly typed local variable using the C# ? token (see Chapter 4 for details on nullable data types):
//Nope, can't define nullable implicit variables,
//as implicit variables can never be initially assigned
//null to begin with!
var? nope = new SportsCar(); var? stillNo = 12;
var? noWay = null;

CHAPTER 13 ■ C# 2008 LANGUAGE FEATURES |
419 |
Implicitly Typed Local Arrays
Closely related to the topic of implicitly typed local variables is the subject of implicitly typed local arrays. Using this technique, you can allocate a new array type without specifying the type contained within the array itself:
static void DeclareImplicitArrays()
{
// a is really int[].
var a = new[] { 1, 10, 100, 1000 }; Console.WriteLine("a is a: {0}", a.ToString());
// b is really double[].
var b = new[] { 1, 1.5, 2, 2.5 }; Console.WriteLine("b is a: {0}", b.ToString());
// c is really string[].
var c = new[] { "hello", null, "world" }; Console.WriteLine("c is a: {0}", c.ToString());
// myCars is really SportsCar[].
var myCars = new[] { new SportsCar(), new SportsCar() }; Console.WriteLine("myCars is a: {0}", myCars.ToString()); Console.WriteLine();
}
Of course, just as when you allocate an array using explicit C# syntax, the items in the array’s initialization list must be of the same underlying type (all ints, all strings, all SportsCars, etc.). Unlike what you might be expecting, an implicitly typed local array does not default to System.Object; thus the following generates a compile-time error:
// Error! Mixed types!
var d = new[] { 1, "one", 2, "two", false };
Implicit Typed Data Is Strongly Typed Data
Be very aware that implicit typing of local variables results in strongly typed data. Therefore, use of the var keyword is not the same technique used with scripting languages (such as VBScript or Perl) or the COM Variant data type, where a variable can hold values of different types over its lifetime in a program (often termed “dynamic typing”).
Rather, type inference keeps the strongly typed aspect of the C# language and affects only the declaration of variables at compile time. After that point, the data point is treated as if it were declared with that type; assigning a value of a different type into that variable will result in a com- pile-time error:
static void ImplicitTypingIsStrongTyping()
{
// The compiler knows "s" is a System.String.
var s = "This variable can only hold string data!"; s = "This is fine...";
//Can invoke any member of the underlying type. string upper = s.ToUpper();
//Error! Can't assign numerical data to a a string! s = 44;
}

420 CHAPTER 13 ■ C# 2008 LANGUAGE FEATURES
Usefulness of Implicitly Typed Local Variables
Now that you have seen the syntax used to declare implicitly typed local variables, I am sure you are wondering when to make use of this construct? First and foremost, using var to declare local variables simply for the sake of doing so really brings little to the table. Doing so can be confusing to others reading your code, as it becomes harder to quickly determine the underlying data type (and therefore more difficult to understand the overall functionality of the variable). Therefore, if you know you need an int, declare an int!
However, as you will see beginning in Chapter 14, the LINQ technology set makes use of query expressions that can yield dynamically created result sets based on the format of the query itself. In these cases, implicit typing is extremely helpful, as we do not need to explicitly define the type that a query may return, which in some cases would be literally impossible to do. Don’t get hung up on the following LINQ example code; however, see if you can figure out the underlying data type of subset:
static void QueryOverInts()
{
int[] numbers = { 10, 20, 30, 40, 1, 2, 3, 8 };
var subset = from i in numbers where i < 10 select i;
Console.Write("Values in subset: "); foreach (var i in subset)
{
Console.Write("{0} ", i);
}
Console.WriteLine();
// Hmm...what type is subset?
Console.WriteLine("subset is a: {0}", subset.GetType().Name); Console.WriteLine("subset is defined in: {0}", subset.GetType().Namespace);
}
I’ll let the interested reader verify the type-of-type of subset by executing the preceding code (and it is not an array of integers!). In any case, it should be clear that implicit typing does have its place within the LINQ technology set. In fact, it could be argued that the only time one would make use of the var keyword is when defining data returned from a LINQ query.
■Source Code The ImplicitlyTypedLocalVars project can be found under the Chapter 13 subdirectory.
Understanding Automatic Properties
As you learned in Chapter 5 during our examination of encapsulation services, .NET programming languages prefer the use of type properties to safely obtain and assign private data fields of a type, rather than using traditional GetXXX() and SetXXX() methods. Consider the following encapsulated string type:
//A Car type using standard property
//syntax.
class Car
{
private string carName = string.Empty; public int PetName

CHAPTER 13 ■ C# 2008 LANGUAGE FEATURES |
421 |
{
get { return carName; } set { carName = value; }
}
}
While defining a C# property is not too problematic, you may agree that when your properties simply assign and return the value straightaway as you see here, it is rather verbose to define backing fields and simple property definitions multiple times. By way of an example, if you are modeling a type that requires 15 private points of field data, you end up authoring 15 related properties that are little more than thin wrappers for encapsulation services.
To streamline the process of providing simple encapsulation of field data, C# 2008 now provides automatic property syntax. As the name implies, this feature will offload the work of defining a private backing field and the related C# property member to the compiler using a new bit of syntax. To illustrate, under C# 2008, the previous Car type could now be defined as follows:
class Car
{
// Automatic property syntax. public string PetName { get; set; }
}
■Note The Visual Studio 2008 “Prop” code snippet has been rewritten to make use of automatic property syntax, rather than the traditional C# property logic (see Chapter 2 for an explanation of code snippets).
At first glance, automatic property syntax might seem as if you were defining an abstract property to be overridden by derived types, given the presence of unimplemented get and set scopes. However, this is not the case. If you did intend to define an abstract property in the Car type, you would need to make use of the C# abstract keyword as follows:
abstract class Car
{
// Abstract property in an abstract base class. public abstract string PetName { get; set; }
}
When defining automatic properties, you simply specify the access modifier, underlying data type, property name, and empty get/set scopes. At compile time, your type will be provided with an autogenerated private backing field and a fitting implementation of the get/set logic.
■Note The name of the autogenerated private backing field is not visible within your C# code base. The only way to see it is to make use of a tool such as ildasm.exe.
Unlike traditional C# properties, however, it is not possible to build read-only or write-only automatic properties. While you might think you can just omit the get; or set; within your property declaration as follows:
//Read-only property? Error! public int MyReadOnlyProp { get; }
//Write only property? Error! public int MyWriteOnlyProp { set; }