#44 – Multiple References to the Same Object

If you assign a variable that points to a reference type to another variable of the same type, both variables end up pointing to the same object in memory.  This means that changing the contents of the object through the first reference results in changes that are also seen by the second reference.

The following code:

 Person p1 = new Person("Sean", 46);     // New Person object
 Person p2 = p1;                         // Points to same object

Results in this situation:

Also, if we change one of the properties of the Person object using the p2 reference, we see the same change when using p1. This confirms that both variables are pointing to the same object.

 p2.Age = 50;
 int age = p1.Age;       // Also now = 50

#43 – Objects Are Instantiated on the Heap

When you instantiate a reference type using the new operator, you are creating a new instance of that type.  The object is created on what’s known as the managed heap.  In other words, memory is allocated to store the member data of the object and that memory is allocated from an area of memory known as the heap.

For example, when you execute the following line to create a new Person object:

  Person myPerson = new Person("Sean", 46);

You’ve created an instance of a Person, storing the data on the heap.  You’ve also created a reference variable, myPerson, that references (or points to) the new object.

#42 – Interacting with an Object

Once you create an instance of a reference type using the new keyword, you can interact with the new object through the variable that serves as a reference to that object.  You can call the object’s methods or read and write its properties.

 // Make a new Person object
 Person p1 = new Person("Sean", 46);

 // Read some properties
 string theName = p1.Name;   // Sean
 int howOld = p1.Age;        // 46

 // Set Age property to new value
 p1.Age = p1.Age - 10;
 int younger = p1.Age;       // Now 36

 // Call method that takes no parameters, returns description
 //   Will return:  Sean is 36 yrs old.
 string describe = p1.Description();

#41 – Instantiating Reference Types Using the new Keyword

To create an object of a particular type, you need to instantiate the type.  Value types are instantiated by assigning them a value.  Reference types are instantiated using the new keyword.  Using new allows us to create a new instance of the type.

When new is used to instantiate a type, the type’s constructor is called to perform the initialization.  The type may have a default constructor that takes no parameters, or it may have a constructor that takes one or more parameter values.  It may also support multiple constructors.

Variables declared as instances of reference types will hold the value null until they are instantiated.

 Person p1;     // Not instantiated, value is null

 // p2 points to new instance of the Person class
 //   Default constructor, takes no parameters
 Person p2 = new Person();

 // Construct another Person object using a
 //   different constructor, which takes Name and Age
 Person p3 = new Person("Sean", 46);

#40 – TrueString and FalseString

The System.Boolean type (bool) provides two fields that let you get string representations of the true and false values.

 string trueText = bool.TrueString;      // "True"
 string falseText = bool.FalseString;    // "False"

These fields return “True” and “False”, regardless of current regional settings or the native language of the operating system.

These values are identical to the string values returned from the System.Boolean.ToString() method, which can be called on an instance of bool.

 bool b1 = true;
 bool b2 = false;

 string true2 = b1.ToString();     // "True"
 string false2 = b2.ToString();    // "False"

#39 – MinValue / MaxValue for Numeric Types

You can access minimum and maximum values for the various built-in numeric types from your code by using the MinValue and MaxValue properties of each type.  All of the built-in numeric types have MinValue and MaxValue properties.

 byte bMin = byte.MinValue;    // 0
 byte bMax = byte.MaxValue;    // 255

 int nMin = int.MinValue;      // -2147483648
 int nMax = Int32.MaxValue;    // 2147483647

 char cMin = char.MinValue;    // 0x0000
 char cMax = char.MaxValue;    // 0xffff

#38 – Data Type Hierarchy

All types in .NET inherit from another type, except for System.Object (object), which is at the top of the class hierarchy.

Each type is one of:

  • A built-in type
    • Part of the Common Type System (CTS)
    • Available in C# using a keyword for the type, e.g. int, float, string, array, object
  • A custom type
    • Defined in the .NET Framework class library
  • A user-defined type
    • You define in your code

Here is the class hierarchy for all types.  Indentation indicates that a class derives from class listed above it.  Parentheses indicate C# keywords for built-in types.

  • System.Object  (object)
    • System.ValueType
      • System.Enum
        • user-defined enum types
      • System.Boolean  (bool)
      • System.Byte  (byte)
      • System.SByte  (sbyte)
      • System.Int16  (short)
      • System.UInt16  (ushort)
      • System.Int32  (int)
      • System.UInt32  (uint)
      • System.Int64  (long)
      • System.UInt64  (ulong)
      • System.Single  (float)
      • System.Double  (double)
      • System.Decimal  (decimal)
      • System.Char  (char)
      • System.Void  (void)
      • System.DateTime
      • System.Guid
      • System.TimeSpan
      • System.IntPtr
      • System.UIntPtr
      • user-defined struct types
    • System.String  (string)
    • System.Array  (array)
    • System.Type
    • System.Delegate
      • System.MulticastDelegate  (delegate)
    • custom types in .NET Framework
    • user-defined types
    • boxed value types

#37 – All Value Types Have a Default Constructor

All built-in value types in C# support a default (parameterless) constructor using the new keyword.  The default constructor allows you to instantiate an object such that it takes on a default value.

You’d normally instantiate one of the built-in types by giving  the associated variable a value.  But it’s also possible to use the new keyword to cause the variable to take on a default value.

 int i;          // Not instantiated yet
 int n1 = 12;    // Instanatiated, w/value of 12
 int n2 = new int();   // Instantiated, w/default value

The default values for the built-in value types are:

  • bool type = false
  • Numeric types (e.g. int, float) = 0 or 0.0
  • char type = single empty character
  • DateTime type = 1/1/0001 12:00:00 AM

#36 – Variable Initialization and Definite Assignment

Variables must be assigned a value before they can be used.  You can declare a variable without initializing it, but attempting to reference the value of the variable before you’ve given it a value will result in a compiler error.

 int i;
 int j = i + 1;   // Error: Use of unassigned local variable 'i'

This requirement of definite assignment before use means that you are never able to reference an unassigned variable in C#.

It’s considered good practice to initialize a variable when you declare it.  This applies to both built-in types and custom types.

When you initialize a variable, the object that it references is said to be instantiated.

 Person p = new Person();
 int i = 0;

#35 – Declaring Variables

In C#, you declare a variable by specifying a type, followed by the name of the new variable.  You can also optionally initialize the variable at the time that you declare it.

If you initialize a variable at the time that it is declared, the initializer can be a literal value, an expression, or an array initializer.

 float length;
 int age = 46;
 string s = "Sean";
 Person p;
 Person q = new Person("Herman", 12);
 object o = s;
 int yrsLeft = 100 - age;
 int[] nums = { 10, 20, 30 };
 object[] stuff = { nums, age, q };