Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

23 Steps To Become a C# Programmer / Developer in 5 Days

C# Tutorial Contents

Introduction:



Welcome to the C# Station Tutorial. This is a set of lessons befitted for beginning to intermediate programmers or anyone who would like to gain comprehension of the C# programming language. These lessons will help you get a quick head-start with C# programming.

To get started, you will need a compiler and an editor. There are several options for obtaining a compiler to write C# programs. A free alternative is to download the .NET Frameworks SDK and use Notepad. Of course, many editors and IDE options are available, so see the Tools section to select the right option. Most of the examples in these tutorials run as console programs. Microsoft Visual Studio is also available in multiple versions and is a free download for Visual Studio Express.

This tutorial is a work in progress. Its quality is a product of volunteer reviews and valuable feedback from many readers. Please visit periodically for the latest updates and new lessons.

Once you’ve completed this tutorial, you may be interested in additional resources to continue learning C#. There are also other Articles on this site.

 References:

23 Steps To Become :








Lesson 12: Structs

This lesson will teach you about the C# struct. Our objectives are as follows:
  • Understand the Purpose of structs.
  • Implement a struct.
  • Use a struct.

What is a struct?

struct is a value type. To help understand the struct, it’s helpful to make a comparison with classes, as described in Lesson 7: Introduction to Classes and subsequent chapters. While a struct is a value type, a class is a reference type. Value types hold their value in memory where they are declared, but reference types hold a reference to an object in memory. If you copy a struct, C# creates a new copy of the object and assigns the copy of the object to a separate struct instance. However, if you copy a class, C# creates a new copy of the reference to the object and assigns the copy of the reference to the separate class instance. Structs can’t have destructors, but classes can have destructors. Another difference between a struct and class is that a struct can’t have implementation inheritance, but a class can, as described in Lesson 8: Class Inheritance. Although a struct can’t have implementation inheritance, it can have interface inheritance, as described in Lesson 13: Interfaces, which is the next lesson following this one. Lesson 22: Topics on C# Type, digs deeper into the differences between value and reference types, providing code that demonstrates the concepts that are introduced here.
The .NET Framework includes many types that are structs, including many of the built-in types. For example, a System.Int32 is a C# int, aSystem.Single is a C# float, and a System.Bool is a C# bool. The C# built-in types are aliases for .NET Framework types, giving you language-specific syntax. If you look at the documentation for any of these .NET Framework types, you’ll see them declared as structtypes. That means you’ll need to recognize what a struct type is when you see it, which the next section helps with by showing you how to create your own custom struct type.

Creating a Custom struct Type

While the behavior of class and struct types are very different, their syntax is similar. You declare the type and its members with the primary visual difference being that a struct uses the keyword struct and a class uses the keyword class. The example in Listing 12-1 demonstrates how to define a custom struct. In this case, the struct is a Rectangle with Width and Height properties, similar to what you might use to represent a rectangular shape on a screen.
Listing 12-1. Defining a struct
/// <summary>
/// Custom struct type, representing
    a rectangular shape
/// </summary>
struct Rectangle
{
    /// <summary>
    /// Backing Store for Width
    /// </summary>
    private int m_width;

    /// <summary>
    /// Width of rectangle
    /// </summary>
    public int Width 
    {
        get
        {
            return m_width;
        }
        set
        {
            m_width = value;
        }
    }

    /// <summary>
    /// Backing store for Height
    /// </summary>
    private int m_height;

    /// <summary>
    /// Height of rectangle
    /// </summary>
    public int Height
    {
        get
        {
            return m_height;
        }
        set
        {
            m_height = value;
        }
    }
}
As you can see, the Rectangle struct in Listing 12-1 looks very much like a class with a couple properties, except that it uses the keywordstruct, instead of the keyword class, to declare that Rectangle is a struct.

Using a struct

To use a struct, instantiate the struct and use it just like a class. Listing 12-2 shows how to instantiate the Rectangle struct and access its properties.
Listing 12-2. Using a Struct
using System;

/// <summary>
/// Example of declaring and using
    a struct
/// </summary>
class StructExample
{
    /// <summary>
    /// Entry point: execution starts
        here
    /// </summary>
    static void Main()
    {
        // instantiate a new Rectangle struct
        // where Width is set to 1 and Height
            is set to 3
 Rectangle rect1 = new Rectangle();
        rect1.Width = 1;
        rect1.Height = 3;

        // show the value of Width and Height
            for rect1
        Console.WriteLine("rect1: {0}:{1}", rect1.Width, rect1.Height);

        Console.ReadKey();
    }
}
The code in the Main method of Listing 12-2 instantiates a new Rectangle struct and sets its Height and Width properties. The experience is similar to how a class can be used. Here’s the output:
rect1: 1:3
An alternate way of instantiating a struct and setting its properties is with an object initializer, shown below:
        // you can also use object
    initialization syntax
        Rectangle rect11 = new Rectangle
        {
            Width = 1,
            Height = 3
        };
Notice that the object initializer uses curly braces and sets properties via a comma-separated list of name/value pairs.

Overloading struct Constructors

The two previous examples of instantiating a struct, via constructor only and via object initializer, used the default (parameterless) constructor of the struct. The default constructor is implicitly defined by C# and you can’t implement the default constructor yourself.  The default constructor initializes all struct fields to default values. i.e. integrals are 0, floating points are 0.0, and booleans are false. If you need custom constructor overloads, you can add new constructors, as long as they have one or more parameters. Listing 12-3 shows a customization of the Rectangle struct from Listing 12-1 that includes a constructor overload.
Listing 12-3: Overloading a struct Constructor
/// <summary>
/// Custom struct type, representing
    a rectangular shape
/// </summary>
struct Rectangle
{
    /// <summary>
    /// Backing Store for Width
    /// </summary>
    private int m_width;

    /// <summary>
    /// Width of rectangle
    /// </summary>
    public int Width 
    {
        get
        {
            return m_width;
        }
        set
        {
            m_width = value;
        }
    }

    /// <summary>
    /// Backing store for Height
    /// </summary>
    private int m_height;

    /// <summary>
    /// Height of rectangle
    /// </summary>
    public int Height
    {
        get
        {
            return m_height;
        }
        set
        {
            m_height = value;
        }
    }

 /// <summary> /// Instantiate rectangle struct with
    dimensions /// </summary>
    /// <param name="width">Width
        to make new rectangle</param> 
            /// <param name="height">Height to make new rectangle</param>
    public Rectangle(int width, 
            int height) { m_width = width; m_height = height; }
}
The highlighted portion of code in Listing 12-3 is a constructor overload. Constructors are named the same as their containing struct, which is Rectangle in this case. This Rectangle constructor overload has two parameters, which it assigns to backing stores that are encapsulated by properties for calling code. Listing 12-4 shows an example of how you would use the constructor overload from Listing 12-3 to instantiate a new Rectangle.
Listing 12-4: Instantiating a struct Through a Constructor Overload
using System;

/// <summary>
/// Example of declaring and using
    a struct
/// </summary>
class StructExample
{
    /// <summary>
    /// Entry point: execution starts
        here
    /// </summary>
    static void Main()
    {
        // instantiate a new Rectangle struct
        // where Width is set to 5 and Height
            is set to 7
 Rectangle rect2 = new Rectangle(5, 7);

        // show the value of Width and Height
            for rect2
        Console.WriteLine("rect2: {0}:{1}", rect2.Width, rect2.Height);

        Console.ReadKey();
    }
}
The code in the Main method of Listing 12-4 instantiates a Rectangle struct and displays the values set via the constructor overload. When instantiating rect2, the code passes the values 5 and 7 as arguments. From the constructor in Listing 12-3, you can see that the Width ofrect2 will be set to 5 and the Height of rect2 will be set to 7. Here’s the output from Listing 12-4:
rect2: 5:7

Adding a Method to a struct

All of the examples so far showed how you can add properties and constructors to a struct, but you can also add methods to a struct. Defining a method in a struct is the same as defining a method in a class. Listing 12-5 shows the Rectangle struct with a method namedAdd.
Listing 12-5: Adding a Method to a struct
/// <summary>
/// Custom struct type, representing
    a rectangular shape
/// </summary>
struct Rectangle
{
    /// <summary>
    /// Backing Store for Width
    /// </summary>
    private int m_width;

    /// <summary>
    /// Width of rectangle
    /// </summary>
    public int Width 
    {
        get
        {
            return m_width;
        }
        set
        {
            m_width = value;
        }
    }

    /// <summary>
    /// Backing store for Height
    /// </summary>
    private int m_height;

    /// <summary>
    /// Height of rectangle
    /// </summary>
    public int Height
    {
        get
        {
            return m_height;
        }
        set
        {
            m_height = value;
        }
    }

    /// <summary>
    /// Instantiate rectangle struct
        with dimensions
    /// </summary>
    /// <param name="width">Width
        to make new rectangle</param>
    /// <param name="height">Height
        to make new rectangle</param>
    public Rectangle(int width, int height)
    {
        m_width = width;
        m_height = height;
    }


    /// <summary> 
    /// Increase the size of this rectangle by the size of the specified rectangle
    /// </summary>
    /// <param name="rect">Rectangle that will be added to this rectangle</param>
    /// <returns>New rectangle created by adding rect to this rectangle</returns>
    public Rectangle Add(Rectangle rect)
    { 
        // create instance of rectangle struct with default constructor
        Rectangle newRect = new Rectangle();

        // add matching axes and assign to new Rectangle struct
        newRect.Width = Width + rect.Width; newRect.Height = Height + rect.Height;

        // return new Rectangle struct
        return newRect; 
    }
}
The highlighted code in Listing 12-5 is a method named Add. It might or might not make sense to add two Rectangle structs together, but the example demonstrates how to define a method in a struct. In this case, the Add method will increase the Height and Width of the current Rectangle instance by adding the Height and Width in the rect parameter. The result of the method is a new Rectangle with the added properties.

Calling a struct Method

You can call the Add method, from Listing 12-5, through an instance of a Rectangle struct. Listing 12-6 shows how to instantiate twoRectangle structs, call the Add method and assign the result of the Add method call to another Rectangle struct.
Listing 12-6: Calling a struct Method
using System;

/// <summary>
/// Example of declaring and using
    a struct
/// </summary>
class StructExample
{
    /// <summary>
    /// Entry point: execution starts
        here
    /// </summary>
    static void Main()
    {
        // instantiate a new Rectangle struct
        // where Width is set to 1 and Height is set to 3
 Rectangle rect1 = new Rectangle();
        rect1.Width = 1;
        rect1.Height = 3;

        // show the value of Width and Height for rect1
        Console.WriteLine("rect1: {0}:{1}", rect1.Width, rect1.Height);

        // instantiate a new Rectangle struct
        // where Width is set to 5 and Height is set to 7
 Rectangle rect2 = new Rectangle(5, 7);

        // show the value of Width and Height for rect2
        Console.WriteLine("rect2: {0}:{1}", rect2.Width, rect2.Height);


        // invoke the Add method on the rect1 Rectangle struct instance,
        // passing the rect2 Rectangle struct instance as an argument
        // and assigning the new copy of the value returned by the
        // Add method to the rect3 Rectangle struct.
        Rectangle rect3 = rect1.Add(rect2);

        // show the value of Width and Height for rect3
        Console.WriteLine("rect3: {0}:{1}", rect3.Width, rect3.Height);

        Console.ReadKey();
   }
}
In the Main method of Listing 12-6, the code instantiates rect1 and rect2, which are both Rectangle structs, assigning values to their Heightand Width properties. The struct instantiation examples should be familiar by now because they are the same as earlier examples. What’s useful about Listing 12-6 is the highlighted code, which shows how to invoke the Add method of the Rectangle struct. The code invokes theAdd method of the rect1 instance and passes rect2 as the Rectangle struct to be added to rect1. The Add method in Listing 12-5 shows what happens when this code executes. In Listing 12-6, the return value of the Add method is assigned to rect3, which is a largerRectangle with each of its sides equal to the sum of the individual sides of rect1 and rect2. Here’s the output:
rect1: 1:3 
rect2: 5:7 
rect3: 6:10

Summary

This lesson described what a struct was and identified a few differences between class and struct types. You learned how to create a struct. You can instantiate a struct either via a default constructor or a custom constructor overload that you write. You also saw how to implement properties and methods in structs.
I invite you to return for Lesson 13: Interfaces.

Lesson 23: Working with Nullable Types

Working with Nullable Types
Working with value types and data can sometimes be challenging because a value type doesn’t normally hold a null value. This lesson shows you how to overcome this limitation with C# nullable types. Here’s what you’ll learn.
  • Understand the problem that nullable types solve
  • See how to declare a nullable type
  • Learn how to use nullable types

Understanding the Problem with Value Types and Null Values

As explained in Lesson 12: Structs, the default value of a struct (value type) is some form of 0. This is another difference between reference types and value types, in addition to what was described in Lesson 22: Topics on C# Type. The default value of a reference type is null. If you’re just writing C# code and managing your own data source, such as a file that holds data for your application, the default values for structs works fine.
In reality, most applications work with databases, which have their own type systems. The implications of working with database type systems is that you don’t have a one-to-one mapping between C# and database types. One glaring difference is that database types can be set to null. A database has no knowledge of reference and value types, which are C# language (.NET Platform) concepts. This means that C# value type equivalents in the database, such as intdecimal, and DateTime, can be set to null.
Since a type in the database can be null, but your C# value type can’t be null, you have to find some way to provide a translation in your C# code to account for null values. Effectively, the scheme you use will often be inconsistent from one program to another; something you often don’t have a choice about. For example, what if you wanted to handle a null DateTime from SQL Server as the minimum DateTimevalue in C#. After that project, your next task would be to read data from a legacy Foxpro database, whose minimum DateTime value is different from SQL Server. Because of this lack of consistency and potential confusion, C# 2.0 added nullable types, which are more elegant and natural for working with null data.

Declaring Nullable Types

To declare a value type as nullable, append a question mark, ?, to the type name. Here’s how to declare a DateTime variable as a nullable type:
    DateTime? startDate;
DateTime can’t normally hold a null value, but the declaration above enables startDate to hold null, as well as any legal DateTime value. The proper terminology is to refer to the type of startDate as a nullable DateTime.
You can assign a normal value to startDate like this:
    startDate = DateTime.Now;
or you can assign null, like this:
    startDate = null;
Here’s another example that declares and initializes a nullable int:
    int? unitsInStock = 5;
The unitsInStock in the example above can be assigned a value of null also.

Working with Nullable Types

When you have nullable types, you’ll want to check them to see if they’re null. Here’s an example that shows how you can check for a nullvalue:
    bool isNull = startDate == null;

    Console.WriteLine("isNull: " + isNull);
The example above shows that you only need to use the equals operator to check for null. You could also make the equality check as part of an if statement, like this:
    int availableUnits;

    if (unitsInStock == null)
    {
        availableUnits = 0;
    }
    else
    {
        availableUnits = (int)unitsInStock;
    }
Note: Notice the cast operator in the else clause above. An explicit conversion is required when assigning from nullable to non-nullable types.
That’s several lines of code for something that appears to be such a common operation. Fortunately, there’s a better way to perform the same task, using the coalesce operator, ??, shown below:
    int availableUnits = unitsInStock ?? 0;
The coalesce operator works like this: if the first value (left hand side) is null, then C# evaluates the second expression (right hand side).

Summary

This lesson explained how nullable types can be useful in your C# applications – especially when working with values from a database. You learned how to declare a nullable type and how to assign values, both null and non-null to nullable types. Another skill you learned was how to use nullable types by checking to see if their values are null. As you saw, the coalesce operator can be useful to help work with nullable type variables when you need to assign a valid value to a non-nullable type.

Lesson 22: Topics on C# Type

C# Type

Throughout this tutorial, you’ve seen many different types, including those that are part of C# and custom designed types. If you’ve taken the samples and worked on them yourself, extending and writing your own programs, you are likely to have experienced errors associated with type. For example, you can’t assign a double to an int without using a cast operator to perform the conversion. Another feature of C# concerns the semantic differences between reference and value types. Such problems should make you wonder why this is so and that’s what this lesson is for. Here are the objectives for this lesson:
  • Understand the need for type safety
  • See how to convert one type to another
  • Learn about reference types
  • Learn about value types
  • Comprehend the semantic differences between reference and value types

Why Type Safety?

In untyped languages, such as scripting languages, you can assign one variable to another and the compiler/interpreter will use an intelligent algorithm to figure out how the assignment should be done. If the assignment is between two variables of the same type, all is good. However, if the assignment is between different types, you could have serious problems.
For example, if you assigned an int value to a float variable it would convert okay because the fractional part of the new float would just be zero. However, if you went the other way and assigned a float value to an int variable, that would most likely be a problem. You would lose all of the precision of the original float value. Consider the damage that could be caused if the float value represented a chemical ingredient, an engineering measurement, or a financial value. Finding such an error would be difficult and particularly expensive, especially if the error didn’t show up until your application was in production (already being used by customers).

Using the Cast Operator for Conversions

In Lesson 02: Operators, Types, and Variables, you learned about C# types and operators. It explained the size and precision of the various types and there is a list of available operators. The cast operator, (x), is listed first as a primary operator in Table 2-4. When you must convert a type that doesn’t fit, it must be done via what is called an explicit conversion, which uses the cast operator. Listing 22-1 has an example of an implicit conversion, which doesn’t require the cast operator, and an explicit conversion.
Listing 22-1. Cast Operators
using System;

class Program
{
    static void Main()
    {
        float lengthFloat = 7.35f;

        // lose precision - explicit conversion
        int lengthInt = (int)lengthFloat;

        // no problem - implicit conversion
        double lengthDouble = lengthInt;

        Console.WriteLine("lengthInt = " + lengthInt);
        Console.WriteLine("lengthDouble = " + lengthDouble);
        Console.ReadKey();
    }
}
Here’s the output:
lengthInt = 7
lengthDouble = 7
Since a floatlengthFloat, has a fractional part but an intlengthInt, doesn’t; the types aren’t compatible. Because of type safety, C# won’t allow you to assign lengthFloat directly to lengthInt, which would be dangerous. For your protection, you must use a cast operator, (int), to force the explicit conversion of lengthFloat to lengthInt. In the output, you can see that lengthInt is 7, showing that it lost the fractional part of the 7.35f value from lengthFloat.
The assignment from lengthInt to lengthDouble is safe because a double is 64-bit and an int is 32-bit, meaning you won’t lose information. Therefore, the conversion is implicit, meaning that you can perform the assignment without the cast operator.

Understanding Reference Types

Reference type variables are named appropriately (reference) because the variable holds a reference to an object. In C and C++, you have something similar that is called a pointer, which points to an object. While you can modify a pointer, you can’t modify the value of a reference – it simply points at the object in memory.
An important fact you need to understand is that when you are assigning one reference type variable to another, only the reference is copied, not the object. The variable holds the reference and that is what is being copied. Listing 22-2 shows how this works.
Listing 22-2. Reference Type Assignment
using System;

class Employee
{
    private string m_name;

    public string Name
    {
        get { return m_name; }
        set { m_name = value; }
    }
}

class Program
{
    static void Main()
    {
        Employee joe = new Employee();
        joe.Name = "Joe";

        Employee bob = new Employee();
        bob.Name = "Bob";

        Console.WriteLine("Original Employee Values:");
        Console.WriteLine("joe = " + joe.Name);
        Console.WriteLine("bob = " + bob.Name);

        // assign joe reference to bob variable
        bob = joe;

        Console.WriteLine();
        Console.WriteLine("Values After Reference Assignment:");
        Console.WriteLine("joe = " + joe.Name);
        Console.WriteLine("bob = " + bob.Name);

        joe.Name = "Bobbi Jo";

        Console.WriteLine();
        Console.WriteLine("Values After Changing One Instance:");
        Console.WriteLine("joe = " + joe.Name);
        Console.WriteLine("bob = " + bob.Name);

        Console.ReadKey();
    }
}
Here’s the output:
Original Employee Values:
joe = Joe
bob = Bob

Values After Reference Assignment:
joe = Joe
bob = Joe

Values After Changing One Instance:
joe = Bobbi Jo
bob = Bobbi Jo
In Listing 22-2, I created two Employee instances, joe and bob. You can see in the output that the Name properties of both Employeeinstances each show their assigned values from when the objects were first created. After assigning joe to bob, the value of the Nameproperties of both instances are the same. This is what you might expect to see.
What might surprise you is the values that occur after assigning a value to the Employee instance variable named joe. If you look at the code closely, you’ll notice that it doesn’t change bob – only Joe. However, the results from the output show that the Name property in bobis the same as the Name property in joe. This demonstrates that after assigning joe to bob, both variables held references to the joeobject. Only the reference was copied – not the object. This is why you see the results of printing Name in both joe and bob are the same because the change was on the object that they both refer to.
The following types are reference types:
  • arrays
  • class’
  • delegates
  • interfaces
Note: The primitive type, string, is also a reference type.

Understanding Value Types

Value type variables, as their name (value) suggests, hold the object value. A value type variable holds its own copy of an object and when you perform assignment from one value type variable to another, both the left-hand-side and right-hand-side of the assignment hold two separate copies of that value. Listing 22-3 shows how value type assignment works.
Listing 22-3. Value Type Assignment
using System;

struct Height
{
    private int m_inches;

    public int Inches
    {
        get { return m_inches; }
        set { m_inches = value; }
    }
}

class Program
{
    static void Main()
    {
        Height joe = new Height();
        joe.Inches = 71;

        Height bob = new Height();
        bob.Inches = 59;

        Console.WriteLine("Original Height Values:");
        Console.WriteLine("joe = " + joe.Inches);
        Console.WriteLine("bob = " + bob.Inches);

        // assign joe value to bob variable
        bob = joe;

        Console.WriteLine();
        Console.WriteLine("Values After Value Assignment:");
        Console.WriteLine("joe = " + joe.Inches);
        Console.WriteLine("bob = " + bob.Inches);

        joe.Inches = 65;

        Console.WriteLine();
        Console.WriteLine("Values After Changing One Instance:");
        Console.WriteLine("joe = " + joe.Inches);
        Console.WriteLine("bob = " + bob.Inches);

        Console.ReadKey();
    }
}
Here’s the output:
Original Height Values: 
joe = 71 
bob = 59 

Values After Value Assignment: 
joe = 71 
bob = 71 

Values After Changing One Instance: 
joe = 65 
bob = 71
In Listing 22-3, you can see that the Inches property of bob and joe are initially set to different values. After assigning joe to bob, a value copy occurs, where both of the variables have the same value, but are two separate copies. To demonstrate value assignment results, notice what happens after setting joe to 65; The output shows that bob did not change, which demonstrates that value types hold distinct copies of their objects.
The following types are value types:
  • enum
  • struct
Note: All of the primitive types (int, char, double, etc.), except for string, are value types.

Reference Type and Value Type Differences

From the previous paragraphs, you might already see that there is a difference reference type and value type assignment. Reference types copy a reference to an object and value types copy the object. If you don’t know this, then the effects can be surprising in your code when performing tasks such as making assignments and passing arguments to methods.

Summary

This lesson provided a few tips on working with types in C#. You should now have a better understanding of type safety and how it can help you avoid problems. This lesson showed you how to use a cast operator to perform conversions and explained the difference between explicit and implicit conversions. You also know that the type system is divided between reference types and value types. To demonstrate the differences between reference types and value types, this lesson provided examples that showed how both reference types and value types behave during assignment.
I invite you to return for Lesson 23: Working with Nullable Types.

Lesson 21: Anonymous Methods

Anonymous Methods

In Lesson 14: Introduction to Delegates, you learned about delegates and how they enable you to connect handlers to events. For C# v2.0, there is a new language feature, called anonymous methods, that are similar to delegates, but require less code. While you learn about anonymous methods, we’ll cover the following objectives:
  • Understand the benefits of anonymous methods
  • Learn how to implement an anonymous method
  • Implement anonymous methods that use delegate parameters

How Do Anonymous Methods Benefit Me?

An anonymous method is a method without a name – which is why it is called anonymous. You don’t declare anonymous methods like regular methods. Instead they get hooked up directly to events. You’ll see a code example shortly.
To see the benefit of anonymous methods, you need to look at how they improve your development experience over using delegates. Think about all of the moving pieces there are with using delegates: you declare the delegate, write a method with a signature defined by the delegate interface, declare the event based on that delegate, and then write code to hook the handler method up to the delegate. With all this work to do, no wonder programmers, who are new to C# delegates, have to do a double-take to understand how they work.
Because you can hook an anonymous method up to an event directly, a couple of the steps of working with delegates can be removed. The next section shows you how this works.

Implementing an Anonymous Method

An anonymous method uses the keyword, delegate, instead of a method name. This is followed by the body of the method. Typical usage of an anonymous method is to assign it to an event. Listing 21-1 shows how this works.
Listing 21-1. Implementing an Anonymous Method
using System.Windows.Forms;

public partial class Form1 : Form
{
    public Form1()
    {
        Button btnHello = new Button();
        btnHello.Text = "Hello";

        btnHello.Click +=
            delegate
            {
                MessageBox.Show("Hello");
            };

        Controls.Add(btnHello);
    }
}
The code in Listing 21-1 is a Windows Forms application. It instantiates a Button control and sets its Text to “Hello”. Notice the combine,+=, syntax being used to hook up the anonymous method. You can tell that it is an anonymous method because it uses the delegatekeyword, followed by the method body in curly braces.
Essentially, you have defined a method inside of a method, but the body of the anonymous method doesn’t execute with the rest of the code. Because you hook it up to the event, the anonymous method doesn’t execute until the Click event is raised. When you run the program and click the Hello button, you’ll see a message box that say’s “Hello” – courtesy of the anonymous method.
Using Controls.Add, adds the new button control to the window. Otherwise the window wouldn’t know anything about the Button and you wouldn’t see the button when the program runs.

Using Delegate Parameters with Anonymous Methods

Many event handlers need to use the parameters of the delegate they are based on. The previous example didn’t use those parameters, so it was more convenient to not declare them, which C# allows. Listing 21-2 shows you how to use parameters if you need to.
Listing 21-2. Using Parameters with Anonymous Methods
using System;
using System.Windows.Forms;

public partial class Form1 : Form
{
    public Form1()
    {
        Button btnHello = new Button();
        btnHello.Text = "Hello";

        btnHello.Click +=
            delegate
            {
                MessageBox.Show("Hello");
            };

        Button btnGoodBye = new Button(); 
        btnGoodBye.Text = "Goodbye";
        btnGoodBye.Left = btnHello.Width + 5; 
        btnGoodBye.Click += 
            delegate(object sender, EventArgs e) 
            { 
                string message = (sender as Button).Text;
                MessageBox.Show(message); 
            };

        Controls.Add(btnHello);
        Controls.Add(btnGoodBye);
    }
}
The bold parts of Listing 21-2 show another Button control added to the code from Listing 21-1. Besides changing the text, btnGoodBye is moved to the right of btnHello by setting it’s Left property to 5 pixels beyond the right edge of btnHello. If we didn’t do this, btnGoodByewould cover btnHello because both of their Top and Left properties would default to 0.
Beyond implementation details, the real code for you to pay attention to is the implementation of the anonymous method. Notice that thedelegate keyword now has a parameter list. this parameter list must match the delegate type of the event the anonymous method is being hooked up to. The delegate type of the Click event is EventHandler, which has the following signature:
public delegate void EventHandler(object sender, EventArgs e);
Notice the EventHandler parameters. Now, here’s how the Button control’s Click event is defined:
public event EventHandler Click;
Notice that the delegate type of the Click event is EventHandler. This is why the anonymous method, assigned to btnGoodBye.Click in Listing 21-2, must have the same parameters as the EventHandler delegate.

Summary

Anonymous methods are a simplified way for you to assign handlers to events. They take less effort than delegates and are closer to the event they are associated with. You have the choice of either declaring the anonymous method with no parameters or you can declare the parameters if you need them.
I invite you to return for Lesson 22: Topics on C# Type.

Lesson 20: Introduction to Generic Collections

Generic Collections
All the way back in Lesson 02, you learned about arrays and how they allow you to add and retrieve a collection of objects. Arrays are good for many tasks, but C# v2.0 introduced a new feature called generics. Among many benefits, one huge benefit is that generics allow us to create collections that allow us to do more than allowed by an array. This lesson will introduce you to generic collections and how they can be used. Here are the objectives for this lesson:
  • Understand how generic collections can benefit you
  • Learn how to create and use a generic List
  • Write code that implements a generic Dictionary

What Can Generics Do For Me?

Throughout this tutorial, you’ve learned about types, whether built-in (intfloatchar) or custom (ShapeCustomerAccount). In .NET v1.0 there were collections, such as the ArrayList for working with groups of objects. An ArrayList is much like an array, except it could automatically grow and offered many convenience methods that arrays don’t have. The problem with ArrayList and all the other .NET v1.0 collections is that they operate on type object. Since all objects derive from the object type, you can assign anything to an ArrayList. The problem with this is that you incur performance overhead converting value type objects to and from the object type and a single ArrayListcould accidentally hold different types, which would cause hard to find errors at runtime because you wrote code to work with one type. Generic collections fix these problems.
A generic collection is strongly typed (type safe), meaning that you can only put one type of object into it. This eliminates type mismatches at runtime. Another benefit of type safety is that performance is better with value type objects because they don’t incur overhead of being converted to and from type object. With generic collections, you have the best of all worlds because they are strongly typed, like arrays, and you have the additional functionality, like ArrayList and other non-generic collections, without the problems.
The next section will show you how to use a generic List collection.

Creating Generic List<T> Collections

The pattern for using a generic List collection is similar to arrays. You declare the List, populate its members, then access the members. Here’s a code example of how to use a List:
    List<int> myInts = new List<int>();

    myInts.Add(1);
    myInts.Add(2);
    myInts.Add(3);

    for (int i = 0; i < myInts.Count; i++)
    {
        Console.WriteLine("MyInts: {0}", myInts[i]);
    }
The first thing you should notice is the generic collection List<int>, which is referred to as List of int. If you looked in the documentation for this class, you would find that it is defined as List<T>, where T could be any type. For example, if you wanted the list to work on string orCustomer objects, you could define them as List<string> or List<Customer> and they would hold only string or Customer objects. In the example above, myInts holds only type int.
Using the Add method, you can add as many int objects to the collection as you want. This is different from arrays, which have a fixed size. The List<T> class has many more methods you can use, such as ContainsRemove, and more.
There are two parts of the for loop that you need to know about. First, the condition uses the Count property of myInts. This is another difference between collections and arrays in that an array uses a Length property for the same thing. Next, the way to read from a specific position in the List<T> collection, myInts[i], is the exact same syntax you use with arrays.
The next time you start to use a single-dimension array, consider using a List<T> instead. That said, be sure to let your solution fit the problem and use the best tool for the job. i.e. it’s common to work with byte[] in many places in the .NET Framework.

Working with Dictionary<TKey, TValue> Collections

Another very useful generic collection is the Dictionary, which works with key/value pairs. There is a non-generic collection, called aHashtable that does the same thing, except that it operates on type object. However, as explained earlier in this lesson, you want to avoid the non-generic collections and use thier generic counterparts instead. The scenario I’ll use for this example is that you have a list ofCustomers that you need to work with. It would be natural to keep track of these Customers via their CustomerID. The Dictionary example will work with instances of the following Customer class:
    public class Customer
    {
        public Customer(int id, string name)
        {
            ID = id;
            Name = name;
        }

        private int m_id;

        public int ID
        {
            get { return m_id; }
            set { m_id = value; }
        }

        private string m_name;

        public string Name
        {
            get { return m_name; }
            set { m_name = value; }
        }
    }
The Customer class above has a constructor to make it easier to initialize. It also exposes its state via public properties. It isn’t very sophisticated at this point, but that’s okay because its only purpose is to help you learn how to use a Dictionary collection.  The following example populates a Dictionary collection with Customer objects and then shows you how to extract entries from the Dictionary:
     Dictionary<int, Customer> customers = new Dictionary<int, Customer>();

    Customer cust1 = new Customer(1, "Cust 1");
    Customer cust2 = new Customer(2, "Cust 2");
    Customer cust3 = new Customer(3, "Cust 3");

    customers.Add(cust1.ID, cust1);
    customers.Add(cust2.ID, cust2);
    customers.Add(cust3.ID, cust3);

    foreach (KeyValuePair<int, Customer> custKeyVal in customers)
    {
        Console.WriteLine(
            "Customer ID: {0}, Name: {1}",
            custKeyVal.Key,
            custKeyVal.Value.Name);
    }
The customers variable is declared as a Dictionary<int, Customer>.  Considering that the formal declaration of Dictionary isDictionary<TKey, TValue>, the meaning of customers is that it is a Dictionary where the key is type int and the value is type Customer. Therefore, any time you add an entry to the Dictionary, you must provide the key because it is also the key that you will use to extract a specified Customer from the Dictionary.
I created three Customer objects, giving each an ID and a Name. I’ll use the ID as the key and the entire Customer object as the value. You can see this in the calls to Add, where custX.ID is added as the key (first parameter) and the custX instance is added as the value (second parameter).
Extracting information from a Dictionary is a little bit different. Iterating through the customers Dictionary with a foreach loop, the type returned is KeyValuePair<TKey, TValue>, where TKey is type int and TValue is type Customer because those are the types that thecustomers Dictionary is defined with.
Since custKeyVal is type KeyValuePair<int, Customer> it has Key and Value properties for you to read from. In our example,custKeyVal.Key will hold the ID for the Customer instance and custKeyVal.Value will hold the whole Customer instance. The parameters in the Console.WriteLine statement demonstrates this by printing out the ID, obtained through the Key property, and the Name, obtained through the Name property of the Customer instance that is returned by the Value property.
The Dictionary type is handy for those situations where you need to keep track of objects via some unique identifier. For your convenience, here’s Listing 20-1, shows how both the List and Dictionary collections work.
Listing 20-1. Introduction to Using Generic Collections with an Example of the List<T> and Dictionary<TKey, TValue> Generic Collections
using System;
using System.Collections.Generic;

public class Customer
{
    public Customer(int id, string name)
    {
        ID = id;
        Name = name;
    }

    private int m_id;

    public int ID
    {
        get { return m_id; }
        set { m_id = value; }
    }

    private string m_name;

    public string Name
    {
        get { return m_name; }
        set { m_name = value; }
    }
}

class Program
{
    static void Main(string[] args)
    {
        List<int> myInts = new List<int>();

        myInts.Add(1);
        myInts.Add(2);
        myInts.Add(3);

        for (int i = 0; i < myInts.Count; i++)
        {
            Console.WriteLine("MyInts: {0}", myInts[i]);
        }

        Dictionary<int, Customer> customers = new Dictionary<int, Customer>();

        Customer cust1 = new Customer(1, "Cust 1");
        Customer cust2 = new Customer(2, "Cust 2");
        Customer cust3 = new Customer(3, "Cust 3");

        customers.Add(cust1.ID, cust1);
        customers.Add(cust2.ID, cust2);
        customers.Add(cust3.ID, cust3);

        foreach (KeyValuePair<int, Customer> custKeyVal in customers)
        {
            Console.WriteLine(
                "Customer ID: {0}, Name: {1}",
                custKeyVal.Key,
                custKeyVal.Value.Name);
        }

        Console.ReadKey();
    }
}
Whenever coding with the generic collections, add a using System.Collections.Generic declaration to your file, just as in Listing 20-1.

Summary

Generic collections give you the best of all worlds with the strong typing of arrays and flexibility of non-generic collections. There are many more generic collections to choose from also, such as StackQueue, and SortedDictionary. Look in the System.Collections.Genericnamespace for other generic collections.
I invite you to return for Lesson 21: Anonymous Methods.