Initializers, Object Initializers, and Anonymous Types
AAHC

Click on A to make all fonts on the page smaller.

Click on A to make all fonts on the page larger.

Click on HC to toggle high contrast mode. When you move your mouse over some bold words in high contrast mode, related words are automatically highlighted. Text is shown in black and white.


In this lesson, we'll continue our discussion of classes by exploring object initialization and creation using the new keyword, and discuss more about constructors and accessors. We'll begin the discussion by exploring the difference between value data types and reference data types.

Types of Data Types
Value Data Types

In a previous lesson, we discussed the use of the parameter modifier ref to pass the parameter by reference instead of by value (the default method). Passing by reference means that any changes made to an argument within a method will be reflected in the calling code. Why? The parameter that uses the ref modifier is really a reference to the variable that was passed in as an argument to the method. In fact, when you add the ref parameter modifer to a parameter, the data type of the parameter is no longer the data type you've indicated in the method signature, but instead a reference to the data type of the parameter.

When thinking about pass by value and pass by reference, remember that some data types are known as value data types, such as integers, floating points, decimal numbers, and booleans. Value data types store the actual value of the data type. Whenever you pass a value data type as a method argument, you're passing a copy of the original variable, or, understood another way, in the method, you simply have the value of the argument, and no reference to the original variable in the calling code.

Reference Data Types

In contrast to value data types, C# contains numerous examples of reference data types, which are effectively objects. When you have a variable that is of a reference data type, the variable does not contain the actual variable data, but instead contains a reference to the location in memory where the object data is stored. Reference data types include strings, classes, arrays (which we'll discuss later), and other C# object constructs.

To help you appreciate why we need a reference data type, consider some of the classes we've created so far in our lessons. Some of these classes had four or five class member variables. When you create an instance of a class with a number of class member variables, each of those variables has memory storage (also known as allocation) needs, depending on their data types. And, if a class member variable is itself a reference data type, such as another class, the embedded class data type may have other class member variables. When we created an instance of the class, our class variable became a reference to the memory location where we created our class, whatever the memory requirements were. It would be very inefficient, and potentially time-consuming, if we had to copy all of that allocated memory every time we passed our class reference variable into a method.

Just as with value data types, when you call a method, and use a reference data type as an argument, you do pass a copy of the reference data type. The difference, though, is that by definition a reference data type is already a reference to a location in memory, so while you're not getting the original reference from the calling program, you are still getting a copy of the memory reference. What does this mean? This means that when you pass a reference data type into a method, without the ref keyword, any changes made in the method to the referenced object will be reflected in the calling program. Changes made in a method to a reference data type are in fact changing the underlying object in memory.

But what happens if you do use the ref parameter modifier with a reference data type? Again, just as with value data types, using ref with a reference data type passes a reference to the original reference variable in the calling program. So, not only can you continue to modify the data for the object wherever it is located in memory, you can also change where in memory the reference refers. Why would you ever want to change the reference location? Frequently, objects are used dynamically, meaning that a variable amount of data needs to be stored, which of course means the amount of memory allocated for an object changes. Using ref with a reference data type allows for the allocation of memory to accomodate the increased memory needs of an expanded object. Once new memory is allocated, the ref reference variable can be switched to point to this new memory location.

NoteYou may be wondering what happens to previously allocated memory when we allocate new memory in a method and reassign a reference variable to point to this new memory location. If the reference parameter used the ref parameter modifier, the old memory is no longer referenced, and eventually will be deallocated by the Common Language Runtime (CLR) garbage collection process. The garbage collector monitors the memory heap for any unreferenced objects, and will release the resources held by these objects. For this reason, objects created in C# are said to be managed, in that the CLR monitors references to the objects.

Boxing and Unboxing

You can convert from a value type to a reference type, and from a reference type to a value type. Converting a value data type to a reference data type is called boxing, and the reverse process unboxing. We won't be exploring boxing and unboxing in this lesson, but you may reference the MSDN Online Reference article Boxing and Unboxing for more information.

The new Keyword

We've seen the new keyword before, so we'll just reiterate that the new keyword with reference data types is used to create memory space for objects on the heap, and invoke object constructors. Using the new keyword is not limited to reference data types, and can be used with value data types; however, as we've learned, value data types do not require an architecture that accomodates the possibility of a variable amount of memory, so for value data types, when you use the new keyword, you are invoking the default constructor for that value data type, and initializing the variable to the default value appropriate for that data type. An example will help.

Value Data Type new Example
// The following lines of C# code are ALL equivalent.            
int myInt = new int();

int myInt = 0;

We also should remind you that all data types in C# are related to underlying .NET data types as part of the Common Type System (CTS). Why is this important? Besides making it possible to create programming languages that ensure data type compatibility, it also means that for every C# data type, there is an underlying .NET data type. Let's add some more equivalent statements to our earlier example.

Value Data Type new Example
// The following lines of C# code are ALL equivalent.            
int myInt = new int();

int myInt = 0;

// CTS System.Int32
Int32 myInt = new Int32();

Int32 myInt = 0;

// Mixing
int myInt = new Int32();

Int32 myInt = new int();

NoteYou may be wondering: if I use new with a value data type like int, is the resulting variable placed on the heap? The answer is no. We'll discuss more about the heap, and the stack, in a later lesson.

Object Initialization

We have a number of mechanisms we can use to initialize properties of an object. We're familiar with two of these mechanisms already: constructors and accessors. We'll revisit both of these methods, and introduce a third mechanism: object initializers.

Constructors and Object Initialization

As we’ve defined our classes, we’ve also defined constructor methods that are called when an instance of a class is created. If multiple constructors are defined, C# determines which constructor to call by matching the fingerprint, or parameters. If no parameters are specified, then the default constructor is called. All classes in C# must have a constructor. If you as the programmer do not define any constructors for a class, C# will implicitly define and call an empty default constructor.

A class represents an object, and as such we use C# syntax to define properties for our class by creating private class member variables. We can initialize these properties through constructors, as seen in the code below.

Constructor Initialization Sample
// Sample code for illustration purposes, will not compile.

class Employee
{
    private int _id;
    private string _lastName;
    private string _firstName;

    public Employee()
    {
        // Default constructor
    }

    public Employee(int id, string lastName, string firstName)
    {
        _id = id;
        _lastName = lastName;
        _firstName = firstName;
    }
}

// How we would initialize object properties using constructor.
Employee employee = new Employee(123, "Lastname", "Firstname");

In the code above, the Employee class defines a default constructor (no parameters), and a constructor that takes three parameters: an employee ID, last name, and first name. When this constructor is called, an Employee object instance can be created setting these three properties. Using a constructor to initialize methods can be called constructor object initialization.

While using a constructor for object initialization is convenient, what happens if we add dozens of properties to the class definition? Rather than using multiple constructors, we can use class accessors.

Accessors and Object Initialization

As class properties expand, we can use class accessors for setting properties of an object. Unlike with a constructor, where we can initialize object properties when we create the object, setting object properties through an accessor requires separate lines of code for each property we want to initialize, as in the example below.

Accessor Initialization Sample
// Sample code for illustration purposes, will not compile.

class Employee
{
    private int _id;
    private string _lastName;
    private string _firstName;
    private DateTime _dateOfBirth;

    public int Id
    {
        get { return _id; }
        set { _id = value; }
    }
    public string LastName {
        get { return _lastName; }
        set { _lastName = value; }
    }
    public string FirstName {
        get { return _firstName; }
        set { _firstName = value; }
    }
    public DateTime DateOfBirth
    {
        get { return _dateOfBirth; }
        set { _dateOfBirth = value; }
    }

    public Employee()
    {
        // Default constructor
    }

    public Employee(int id, string lastName, string firstName)
    {
        _id = id;
        _lastName = lastName;
        _firstName = firstName;
    }
}

// How we would initialize object properties using accessors.
Employee employee = new Employee();
employee.Id = 123;
employee.LastName = "Lastname";
employee.FirstName = "Firstname";
employee.DateOfBirth = Convert.ToDateTime("3/1/1990");

In the example above, we've added accessors for the prior class properties, as well as adding a new accessor for DateOfBirth. To set the date of birth property, we would access this new accessor. The sample initialization code has also been modified to set all of the object properties using accessors, so we really don't need the second constructor.

Object Initializers

If an object has more and more properties, we could define a number of constructors with different parameters, but the possible combination of parameters quickly becomes unmanageable. Of course, we could use accessors for each new property, but as we've seen, initializing properties requires a separate line of code for each property. To address this issue, C# supports the concept of object initialization.

Object initializers employ a syntax that calls the default constructor, and then calls each accessor for properties we've indicated we want to set. We've modified our sample code below, removing the unneeded constructor.

Object Initializer Sample
// Sample code for illustration purposes, will not compile.

class Employee
{
    private int _id;
    private string _lastName;
    private string _firstName;
    private DateTime _dateOfBirth;
    
    public int Id
    {
        get { return _id; }
        set { _id = value; }
    }
    public string LastName {
        get { return _lastName; }
        set { _lastName = value; }
    }
    public string FirstName {
        get { return _firstName; }
        set { _firstName = value; }
    }
    public DateTime DateOfBirth
    {
        get { return _dateOfBirth; }
        set { _dateOfBirth = value; }
    }
                
    public Employee()
    {
        // Default constructor
    }
}

// How we would initialize properties using object initializers
Employee employee = new Employee() 
{
    Id = 123, 
    LastName = "Lastname", 
    FirstName = "Firstname",
    DateOfBirth = Convert.ToDateTime("3/1/1990")
};

As you can see, we didn't change the class definition, other than removing the unneeded constructor. You can also see that after we call the new operator, we then specify the properties we want to set, and the value for each property. We can include all of the properties, or just the ones we want to set.

You'll notice that we've highlighted the parentheses as (). Why? For object initializers, by default, they use the default constructor. When using the default constructor, you don't need to specify the parentheses when using the object initializer syntax, and omitting the parentheses is very common. You can use object initialization with constructors that are not the default constructor, which would of course require the parentheses.

NoteRemember that C# will provide a default constructor if you don't provide one, but this only happens if you have not created any constructors. If you create a constructor with parameters but do not include a default constructor, and then attempt to create the object with no parameters, you'll get an error message indicating that your class does not contain a constructor that takes 0 arguments.

Automatic Properties

C# supports a different syntax for class properties called automatic properties. Previously, we've prefixed all class variables with an underscore, and then created accessors with the same name as the variable, replacing the lowercase letter with an uppercase letter and removing the underscore.

With automatic properties, you don't need to explicitly declare a class variable. However, you should only use automatic properties when you do not need any code in the property get or set bodies. If you do need additional logic, you should use the private class variable technique (also known as full property syntax) we've used throughout the lessons. To access the property from within your class, use the name of the property. The sample code below illustrates a rewrite of the Employee class using automatic properties. In all cases, you'll notice that we've removed the private class variables.

Object Initializers Using Automatic Properties
class Employee
{
    public int Id { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
    public DateTime DateOfBirth { get; set; }

    public Employee()
    {
        // Default constructor
    }
}

Many C# programmers prefer using automatic properties over explicit variables using the underscore prefix. If you later decide you need coding logic with your automatic properties, you'll need to convert your automatic property to the full property syntax. Currently, there is no automatic refactoring option in Studio to make this conversion, so you'll have to make the conversion yourself.

You might wonder why you would use an automatic property over a public class variable, or field. Essentially, automatic properties appear to be simply a more lengthy coding mechanism to store and retrieve data from a class variable, but automatic properties offer many advantages over public class fields. Some of the advantages listed will be covered in later lessons.

  • Automatic properties are a more concise coding style over full property syntax.
  • Properties accept a breakpoint when debugging, but fields do not.
  • Support read-only and/or write-only accessibility.
  • Conversion from an automatic property to a full property with private class variable does not represent a change in the compiled contract between any code that accesses the property, whereas changing from a field to a property does break the contract.
  • The concept of reflection, an advanced topic, works the same with automatic and full properties, but differs between fields and properties.
  • Data binding is possible to properties, but not to fields.

For the rest of this lesson, we'll continue to use the explicit variable definitions with the underscore prefix, or full property syntax, although we'll insert automatic properties in later lessons.

Coding: The Employee Database

Let's tie everything together with an employee database program to work with the concepts we've covered in the lesson.

Create a new Visual Studio Windows Forms Application named EmployeeDatabase. Change the name Form1.cs to EmployeeDatabase.cs, and change the title bar's Text property to Employee Database. Click to save your changes.

Now let's add the controls we'll need.

Modify the form by adding the controls listed below. Change each control Name property to match the Name Property column, and each Text property to match the Text Property column. Arrange the controls as shown.

ObjectName PropertyText Property
LabellastNameLabelLast Name:
LabelfirstNameLabelFirst Name:
LabeldateOfBirthLabelDate Of Birth:
LabelemployeeCountLabelEmployee Count:
TextBoxlastNameTextBox 
TextBoxfirstNameTextBox 
TextBoxdateOfBirthTextBox 
LabelemployeeCountValueLabel0
ButtonaddEmployeeButtonAdd
ButtonexitButtonExit

to save your changes.

Add the following events to the EmployeeDatabase Form by double-clicking each Button control.

ControlEventEvent Name
addEmployeeButtonClickaddEmployeeButton_Click
exitButtonClickexitButton_Click

to save your changes. Next, let's add an Employee class.

In the Solution Explorer, right-click the EmployeeDatabase project and select Add | Class. In the Add New Item dialog box, change the Name property to Employee.cs and click Add. In the EmployeeDatabase.cs Code Editor, modify the code according as shown below.

Employee.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EmployeeDatabase
{
    public class Employee
    {
        private int _id;
        private string _lastName;
        private string _firstName;
        private DateTime _dateOfBirth;
        
        public int Id
        {
            get { return _id; }
            set { _id = value; }
        }
        public string LastName {
            get { return _lastName; }
            set { _lastName = value; }
        }
        public string FirstName {
            get { return _firstName; }
            set { _firstName = value; }
        }
        public DateTime DateOfBirth
        {
            get { return _dateOfBirth; }
            set { _dateOfBirth = value; }
        }
        
        public Employee()
        {
            // Default constructor
        }
    }
}

Click to save your changes. The Employee class should look very similar to the sample code we were using before, and in fact, it's identical code. So, let's switch back to the EmployeeDatabase.cs file, and work with the Employee class to illustrate object initializers.

Select the EmployeeDatabase.cs Code Editor, and modify the code again, as shown below.

EmployeeDatabase.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace EmployeeDatabase
{
    public partial class EmployeeDatabase : Form
    {
        private List<Employee> _employees = new List<Employee>();
        
        public EmployeeDatabase()
        {
            InitializeComponent();
        }
        
        private void addEmployeeButton_Click(object sender, EventArgs e)
        {
            // Increment employee counter.
            int employeeCount = _employees.Count + 1;
            
            // Validate date of birth.
            DateTime dateOfBirth;
            if (!DateTime.TryParse(dateOfBirthTextBox.Text, out dateOfBirth))
            {
                dateOfBirth = DateTime.MinValue;
            }
            
            // Use object initializers through default constructor.
            Employee employee = new Employee()
            {
                Id = employeeCount,
                LastName = lastNameTextBox.Text,
                FirstName = firstNameTextBox.Text,
                DateOfBirth = dateOfBirth
            };
            
            // Add new employee object.
            _employees.Add(employee);
            
            // Update the employee count display
            employeeCountValueLabel.Text = employeeCount.ToString();
        }
        
        private void exitButton_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}

Click and to save your changes and run the program. Enter a last and first name and a date of birth and click Add. The employee count should update, and continue to update if you add more employees. When you're satisfied that everything works, click Exit. Let's discuss how this code works.

OBSERVE: EmployeeDatabase.cs
.
.
.
namespace EmployeeDatabase
{
    public partial class EmployeeDatabase : Form
    {
        private List<Employee> _employees = new List<Employee>();
        
        public EmployeeDatabase()
        {
            InitializeComponent();
        }
        
        private void addButton_Click(object sender, EventArgs e)
        {
            // Increment employee counter.
            int employeeCount = _employees.Count + 1;
            
            // Validate date of birth.
            DateTime dateOfBirth;
            if (!DateTime.TryParse(dateOfBirthTextBox.Text, out dateOfBirth))
            {
                dateOfBirth = DateTime.MinValue;
            }
            
            // Use object initializers through default constructor.
            Employee employee = new Employee()
            {
                Id = employeeCount,
                LastName = lastNameTextBox.Text,
                FirstName = firstNameTextBox.Text,
                DateOfBirth = dateOfBirth
            };
            
            // Add new employee object.
            _employees.Add(employee);
            
            // Update the employee count display
            employeeCountValueLabel.Text = employeeCount.ToString();
        }
.
.
.
    }
}

Most of the code should be pretty familiar to you by now. We create an Employee list object named _employees. When the Add button is clicked, we validate the date of birth, and if the date is invalid, we set the employee's date of birth to the minimum date value. Why? A DateTime data type is a value data type, and as such cannot be set to null. We may want to consider creating our own date of birth class, or changing the date of birth to a string and checking for an empty string, to account for when no date of birth is given. Of course, we could also make it required, and force the user to enter a valid date.

Next, we come to the object initializer code, where we create an employee instance, and setting each of the object properties, including Id, LastName, FirstName, and DateOfBirth. As mentioned before, using the object initializer syntax allows us to set all of the properties, or only the properties we want. After creating the object, we add the employee object to the _employees List, and update the count of the number of employees.

So, as you can see, object initializers are a great alternative to creating numerous constructors, adding a great deal of flexibility to object initialization without adding a lot of cumbersome code to our classes.

Anonymous Types

We have one final topic to cover that uses object initialization: anonymous types. An anonymous type is exactly what the name implies, an object with an implicit type (thus anonymous). Below is an example of an anonymous type.

Anonymous Type
// Create an anonymous type            
var employee = new {             
    Id = 123, 
    LastName = "Lastname", 
    FirstName = "Firstname",
    DateOfBirth = Convert.ToDateTime("3/1/1990")
};

// Output the LastName property of the anonymous type
Console.WriteLine("The employee last name is " + employee.LastName);

As you can see from our object declaration, we're using a new C# keyword, var. We then specify the variable name, employee, then use the new keyword without a class name to create an object instance. We also include object initializers to declare read-only properties. Note that we did say read-only: once instantiated and initialized, anonymous properties cannot be modified.

Once created, an anonymous type instance can only be referenced through the anonymous type instance variable. In our example, the anonymous type instance variable is employee. Anonymous types only include read-only properties. No other class functions, such as events or methods, are allowed. Anonymous instance variables must not be null. The name of the anonymous data type is not accessible to C#, which means you also can't create other variables from an anonymous type. If you need a data type name, use a class.

When would you use an anonymous type? Anonymous types are useful from creating "on-the-fly" objects that we only need to access for a short time. We'll see uses of anonymous data types when we talk later about arrays, and about the .NET query language LINQ.

Popup Forms and Dialogs

We've created popup forms before, even changing them to be Dialog boxes. We want to further explore how to make a popup form as part of expanding your knowledge of how to use C#. Also, you may find this information in your homework projects.

A popup form is used to prompt the user for data, or to make a decision. Popup forms may be modal, which means that the focus of an application is with the popup form, not allowing you to change to any other part of the application, until you dismiss the popup form. If a popup form is not modal, it is modeless, which means you can switch back to the application without dismissing the popup form.

When creating a popup form, we add a new Windows Form to our application, and modify the form just as we have been doing throughout our lessons with the main form that is provided by C# automatically when we create a Windows forms application. To instantiate an instance of the form, we use the new operator, then call either the ShowDialog() method of the form to display the form as a modal popup, or the Show() method of the form to display the form as a modeless popup.

Windows forms, when dismissed, such as when you click an OK or Cancel button, may return a DialogResult result that the calling code may evaluate to determine what needs to happen next. For any Button on a form, you can change the DialogResult property to any of the possible choices: None, OK, Cancel, Abort, Retry, Ignore, Yes, or No. In fact, if you set the DialogResult property of the Cancel Button control to Cancel, when you click on the Cancel Button, you won't need to add an event handler that calls the form's Close() method. For the OK Button control, you could also set the DialogResult property to OK, but a better solution is to set the DialogResult property in the OK Button event handler and call the Close() method once you've verified that everything on the Form is correct. In other words, you may want to validate the form after the user clicks the OK button, and determine if you want to actually close the Form. If there are errors, you may want to display some type of error alert, and not set the DialogResult or Close the Form.

The code below illustrates how to create a popup form, display the form as a modal dialog box, and then check to see if the DialogResult value was OK.

Modal Form
// Declare an EmployeeForm variable, create an instance of the EmployeeForm Form,
// and assign the new EmployeeForm instance object to the variable            
EmployeeForm employeeForm = new EmployeeForm();

// Display the employeeForm instance, and assign the return results to
// a DialogResult variable
DialogResult dialogResult = employeeForm.ShowDialog();
            
// Check the return value, and extract any data             
if (dialogResult == DialogResult.OK)
{
    // Extract the employee information from the Form using the accessor
    Employee employee = employeeForm.EmployeeInfo;
    // Update the employee ID 
    employee.Id = _employees.Count + 1;
    // Add the employee object to our List
    _employees.Add(employee);
    // Update a ListBox that displays the employee information
    displayEmployees();
}

In addition to a form, you can use the C# MessageBox class for simple user responses, like Yes or No. The MessageBox has numerous overloaded methods. We're going to demonstrate the method with parameters for the text to display in the dialog box, text for the caption of the dialog box, and which buttons to display. The code snippet below also includes capturing the result using the DialogResult class.

MessageBox
DialogResult dialogResult = MessageBox.Show("Delete this employee?", "Delete Employee", MessageBoxButtons.YesNo);            

This short tutorial on popup Forms and dialogs should help you as you continue to learn C# and create Windows Forms based applications.

Before you move on to the next lesson, do your homework! Right-click in the window where this lesson text appears and select Back. Then select Quiz for this lesson in the syllabus and answer the quiz questions. When you finish the quiz questions, click HAND IT IN at the bottom of that window. Then do the same with the Project(s) for the lesson. Your instructor will grade your quiz(zes) and project(s) and provide guidance if needed.