Data Types and Keywords

There are two broad categories of variables we'll define: value types and reference types. Value type variables have a strong connection to a single piece of memory stored somewhere. This includes enumerations, primtive data types (int, bool, char), and programmer-defined structs.

Reference types do not contain values themselves but instead an address to where values can be found. The simplest example of this is an array: basically a pointer to memory where a contiguous block of memory is found, containing a collection of homogeneous data. Objects from a class, delegates, strings, and variables referring to dynamically allocated memory, are all reference types.



"enum" is the keyword for defining an enumeration, a programmer-defined, public type that consists of a set of named constants called the "enumerator list". For example:

enum Doctor { Tennant, Smith, Capaldi, Whittaker };

Whereby Doctor.Tennant would have an integer value of 0, Smith is 1, Whittaker is 3. However, you can alter this indexing scheme by doing the following:

enum Doctor { Tennant = 1, Smith, Capaldi, Whittaker };

Whereby all members of the enumeration would begin counting from 1 instead of zero, for you godless heathens out there.

In this context, the fact that Doctor.Tennant has an integer value of 0, does next to nothing when it comes time to output this information. Instead, you will likely want to do something like...

System.Console.WriteLine("The current Doctor is {0}", ((Doctor) current).ToString());

Where current is a enum type of variable, presumably with a well-defined value.

Interestingly, the .NET Framework doesn't have any intrinsic check for whether an enumeration value has been defined or not. For the above example, if I were to assign the value of (Doctor)4, the print statement would simply print 4. If you're anything like me, then this bothers you, as how might you protect your code from bad data or (more likely the case) bad user input? This person from StackOverflow.com devised a series of methods used to check whether an enumeration value is valid and if not, throws a custom "InvalidCastException". I'm a fan of this work!

For those of you coming from a C++ background, the object data type should be new. object is the most general way to describe any type of data that is stored in memory. That is, all memory you use, from integers to strings to instantiations of the Slacker class, can be referred to quite generally as objects. A value type variable can be implicitly converted to the object type by "boxing" it.

int taxicab = 1729;
object slacker = taxicab; // This "boxes" slacker into an integer type
int slackest = (int)slacker; // This "unboxes" the slacker,
// possible only because it was previously "boxed"



readonly is a subtle qualifer, that functions very similiarly to const. They both permit a variable to be given an initial value and then to never have that value altered by your program. But const variables are statically stored in memory, as compared to readonly which acts more like a reference to where that value will be found. The difference comes whenever you want to update those values, and which assemblies need to be re-compiled in order to correctly update.

As a rule of thumb: if you're defining a value you expect will truly never change in the future (e.g. there will only ever be four seasons in any given year, 3.14159 will always approximate pi), define it as const. For other values that may be subject to change (e.g. background colors, margins, or the upper limit to the amount of baloney I will tolerate), define them as readonly .



nullable types bring an interesting twist to the table. These are extensions to the normal data types available to us, with the addition of allowing them to take on null values. To define a variable as nullable, you can do either of the following:

float? myVar;
Nullable<float> myOtherVar;


Now, I'm a big advocate for being a lazy programmer, such that you write as little code as possible in order to achieve your objectives. I will abuse aliases, define an excessive number of functions, and try to generalize my code as best as possible to minimize how much I type, and certainly to reduce how much I have to re-type. While my knee-jerk reaction to learning that both declarations serve the same goal, you should be able to see a value with explicitly typing out the Nullable<T> business, because it does a better job of self-documenting your code. It stands out more than just a ?, which you might overlook as you're browsing through your program for the 4,823rd time, trying to figure out why you're getting a NullPointerException.

Since both declarations work exactly the same way, I expect no one will ever type out Nullable<T>. Why burden yourself with typing out all those extra characters when a ? suffices? Love yourself enough to use the ?.

You can use the HasValue property of the nullable type to determine if the variable contains a value (true) or is equal to null (false). There's also a Value property that returns the — you guessed it! — value. It makes sense to only use this if you know it isn't already null. For example:

float? alpha = null;
float? beta = 2.718;

if (beta.HasValue) // Would evaluate to true
System.Console.WriteLine(beta.Value);


There's also the ?? notation you can use to define a default value when assigning a nullable type to a non-nullable type. For example:

int? slacker = null;
int  test = slacker ?? -1;
// This means: if slacker is not null, set test equal to its value. If it is null, set test to -1


Generally speaking, you'll want to use nullable types as an alternative to defining sentinel values (i.e. "this integer is said to be undefined if equal to -99999) or whenever you are managing a database system, as you will often have attributes of tables defined as possibly being null.