Appendix A. Answers to Quizzes and Exercises

Chapter 1: C# and .NET Programming

Quiz

Solution to Question 1–1.

The Common Language Runtime is the platform for .NET applications that allow you to develop your application in any .NET languages (C#, Visual Basic 2005, etc.) and have them all run seamlessly on a single platform.

Solution to Question 1–2.

Console, Windows, Web, and Web Services.

Solution to Question 1–3.

The Framework specifies how .NET constructs intrinsic types, classes, interfaces, etc.

Solution to Question 1–4.

The Framework Class Library provides a very large suite of predefined classes that you can use in building your application.

Solution to Question 1–5.

This refers to “type safety”—the ability of the compiler to ensure that the objects you create are of the expected type.

Solution to Question 1–6.

Keywords are reserved for use by the language and cannot be used to identify objects or methods you create.

Solution to Question 1–7.

Namespaces are used to ensure that identifiers are unique across libraries of classes.

Solution to Question 1–8.

The job of the compiler is to turn your source code into MSIL.

Solution to Question 1–9.

The Microsoft Intermediate Language is the native language for .NET and is compiled into an executable application by the JIT.

Solution to Question 1–10.

The Just In Time compiler turns your MSIL code into an application in memory.

Exercise

Solution to Exercise 1-1.

The instructions for creating this program are found in Chapter 1.

Chapter 2: Visual Studio 2005

Quiz

Solution to Question 2–1.

A project results in the production of an executable or a library. Most solutions consist of a single project, but many consist of two or more.

Solution to Question 2–2.

Click and drag on the title bar; use the indicators for placement.

Solution to Question 2–3.

Toggles between lock in place, and hide as a tab.

Solution to Question 2–4.

F5 indicates run with debugging; Ctrl-F5 indicates run without debugging.

Solution to Question 2–5.

Allows you to store more than one item on the clipboard.

Solution to Question 2–6.

Ctrl-Shift-V cycles through all the selections.

Solution to Question 2–7.

Allows you to search for symbols (namespaces, classes, and interfaces) and their members (properties, methods, events, and variables).

Solution to Question 2–8.

A tool for returning to a specific place in your code.

Solution to Question 2–9.

An editing tool to help you find the right method and/or the correct parameters and much more.

Solution to Question 2–10.

A complete outline of a commonly used programming structure with replaceable items to speed development.

Exercises

Solution to Exercise 2-1.

Insert a bookmark before the Console.Writeline() statement in Hello World. Navigate away from it and then use the Bookmarks menu item to return to it.

Your Visual Studio window should look something like Figure A-1.

Solution to Exercise 2-3.

Insert a code snippet for a for loop from the Edit → Intellisense menu into your Hello World program. (It won’t do anything for now; you’ll learn about for loops in Chapter 4.)

Your Visual Studio window should look something like Figure A-3.

Chapter 3: C# Language Fundamentals

namespace fundamentals
{
   class exercise
   {
      static void Main(  )
      {
         int myInt = 3;
         float myFloat = 4.25f;
         double myDouble = 123456789.9867;
         char myChar = 'A';
         System.Console.WriteLine("myInt: {0}, myFloat: {1}, myDouble: {2},
            myChar: {3}.", myInt, myFloat, myDouble, myChar);
      }
   }
}

The output should look like this:

myInt: 3, myFloat: 4.25, myDouble: 123456789.9876, myChar: A.
namespace fundamentals
{
   class exercise
   {
      static void Main()
      {
         const float pi = 3.14159f;

         int myInt = 3;
         float myFloat = 4.25f;
         double myDouble = 123456789.9867;
         char myChar = 'A';
         pi = 3.1f;
         System.Console.WriteLine("Round 1: myInt: {0}, myFloat: {1},
            myDouble: {2}, myChar: {3}.  Pi: {4}", myInt, myFloat,
            myDouble, myChar, pi);

      }
   }
}

This program won’t compile, because you’re trying to assign a value to a constant. Instead, you receive a compiler error that reads, “The left-hand side of an assignment must be a variable, property or indexer.”

Chapter 4: Operators

namespace operators
{
   class exercise
   {
      static void Main(  )
      {
         int x = 25;
         int y = 5;
         System.Console.WriteLine("sum: {0}, difference: {1},
            product: {2}, quotient: {3}, modulus: {4}.", x + y, x - y,
            x * y, x / y, x % y);
      }
   }
}

The output looks like this:

sum: 30, difference: 20, product: 125, quotient: 5, modulus: 0.
Solution to Exercise 4-2.

What will be the output of the following method?

static void Main(  )
{
   int varA = 5;
   int varB = ++varA;
   int varC = varB++;
   Console.WriteLine( "A: {0}, B: {1}, C: {2}", varA, varB, varC );
}

The output looks like this:

A: 6, B: 7, C: 6
Solution to Exercise 4-3.

Write a program that demonstrates the difference between the prefix and postfix operators.

namespace operators
{
   class exercise
   {
      static void Main(  )
      {
         int myInt = 5;
         int myOtherInt = myInt;
         System.Console.WriteLine("initial values: myInt: {0},
            myOtherInt: {1}\n", myInt, myOtherInt);

         // prefix evaluation
         myOtherInt = ++myInt;
         System.Console.WriteLine("prefix evaluation myInt: {0},
            myOtherInt: {1}\n", myInt, myOtherInt);

         // postfix evaluation
         myInt = 5;
         myOtherInt = 5;
         myOtherInt = myInt++;
         System.Console.WriteLine("postfix evaluation myInt: {0},
            myOtherInt: {1}\n", myInt, myOtherInt);
      }
   }
}

The output looks like this:

initial values: myInt: 5, myOtherInt: 5
prefix evaluation myInt: 6, myOtherInt: 6
postfix evaluation myInt: 6, myOtherInt: 5

Chapter 5: Branching

Quiz

Solution to Question 5–1.

if, else, switch.

Solution to Question 5–2.

False. In C#, an if statement’s condition must evaluate to a Boolean expression.

Solution to Question 5–3.

The braces make maintenance easier. If you add a second statement later, you are less likely to create a logic error because it is obvious what “block” of statements the if refers to.

Solution to Question 5–4.

Either a numeric value or a string.

Solution to Question 5–5.

False. If the statement has no body, then you can fall through. For example:

case morning:
case afternoon:
      someAction(  );
      break;
Solution to Question 5–6.

Two uses of goto are:

Solution to Question 5–7.

do...while evaluates its condition is at the end of the statement rather than at the beginning, and thus is guaranteed to run at least once.

Solution to Question 5–8.

In a loop, it causes the remainder of the body of the loop to be skipped and the next iteration of the loop to begin immediately.

Solution to Question 5–9.

Two ways of creating an infinite loop are:

for (;;)
while(true)
Solution to Exercise 5-1.

Create a method that counts from 1-10 using each of the while, do...while, and for statements.

using System;
class Exercises
{
   static void Main(  )
   {
      Console.WriteLine( "while" );
      int counter = 1;
      while ( counter <= 10 )
      {
         Console.Write( counter );
         if ( counter < 10 )
         {
            Console.Write( ", " );
         }
         ++counter;
      }

      Console.WriteLine( "\nDo..while" );
      counter = 1;
      do
      {
         Console.Write( counter );
         if ( counter < 10 )
         {
            Console.Write( ", " );
         }
         ++counter;
      } while ( counter <= 10 );

      Console.WriteLine( "\nfor" );
      for ( int ctr = 1; ctr <= 10; ctr++ )
      {
         Console.Write( counter );
         if ( counter < 10 )
         {
            Console.Write( ", " );
         }
      }
      Console.WriteLine( "\nDone" );
   }
}
Solution to Exercise 5-2.

Create a program that evaluates whether a given input is odd or even, a multiple of 10, or too large (over 100) by using four levels of if statement. Then expand the program to do the same work with a switch statement.

using System;
class Exercises
{
    enum numericCondition
    {
        even,
        multiple,
        odd,
        tooBig,
        unknown,
        zero,
    };

    static void Main()
   {
          // possible input conditions


       while ( true )
      {
         // entering any symbol except a number will throw an exception
         // -- no error handling in this simple example
         Console.Write( "Enter a number please: " );
         string theEntry = Console.ReadLine();
         int theNumber = Convert.ToInt32(theEntry) ;
         Console.Write( "NestedIf {0}: ", theNumber );

         // Logic: if the number is greater than 100, say it is too big
         // if it is even but not a multiple of 10 say it is even
         // if it is a multiple of ten, say so
         // if it is not even, say it is odd
         if ( theNumber <= 100 )
         {
           if ( theNumber % 2 == 0 )
           {
              if ( theNumber == 0 )
              {
                 Console.WriteLine( "zero is not even or odd nor a multiple of 10" );
              }
              else
              {
                 if ( theNumber % 10 == 0 )
                 {
                    Console.WriteLine( "You have picked a multiple of 10" );
                 }
                 else
                 {
                    Console.WriteLine( "Your number is even" );
                 }  // end else not a multiple of 10
              }  // end else not zero
           }  // end if even
           else
           {
              Console.WriteLine( "What an odd number to enter" );
           }
        }  // end if not too big
        else
        {
           Console.WriteLine( "Your number is too big for me." );
        }



        Console.Write( "SwitchMethod {0}: ", theNumber  );


   // same logic, different implementation
      // set the enumerated condition
      numericCondition condition = numericCondition.unknown;  // initialize
      condition = ( theNumber  % 2 == 0 ) ?
            numericCondition.even : numericCondition.odd;
      if ( theNumber % 10 == 0 ) condition = numericCondition.multiple;
      if ( theNumber == 0 ) condition = numericCondition.zero;
      if ( theNumber > 100 ) condition = numericCondition.tooBig;

      // switch on the condition and display the correct message
      switch ( condition )
      {
         case numericCondition.even:
            Console.WriteLine( "Your number is even" );
            break;
         case numericCondition.multiple:
            Console.WriteLine( "You have picked a multiple of 10" );
            break;
         case numericCondition.odd:
            Console.WriteLine( "What an odd number to enter" );
            break;
         case numericCondition.tooBig:
            Console.WriteLine( "Your number is too big for me." );
            break;
         case numericCondition.zero:
            Console.WriteLine( "zero is not even or odd nor a multiple of 10" );
            break;
         default:
            Console.WriteLine( "I'm sorry, I didn't understand that." );
            break;
      }
   }
  }
}
Solution to Exercise 5-3.

Create a program that initializes a variable i at 0 and counts up, and initializes a second variable j at 25 and counts down. Use a for loop to increment i and decrement j simultaneously. When i is greater than j, end the loop and print out the message “Crossed over!”.

using System;
class Exercises
{
   static void Main(  )
   {
      int i = -1;
      int j = -1;
      for (i = 0, j = 25; i < j; ++i, --j )
      {
         Console.WriteLine("i: {0}; j: {1}", i, j);
      }
      Console.WriteLine( "Crossed over! i: {0}; j: {1}", i, j );

   }
}

The output looks like this:

i: 0; j: 25
i: 1; j: 24
i: 2; j: 23
i: 3; j: 22
i: 4; j: 21
i: 5; j: 20
i: 6; j: 19
i: 7; j: 18
i: 8; j: 17
i: 9; j: 16
i: 10; j: 15
i: 11; j: 14
i: 12; j: 13
Crossed over! i: 13; j: 12

Chapter 6: Object-Oriented Programming

Quiz

Solution to Question 6–1.

New (user-defined) types are most often created in C# with the keyword class.

Solution to Question 6–2.

A class defines a new type; an object is an instance of that type.

Solution to Question 6–3.

Making your member fields private allows you to change how you store that data (as a member variable, in a database) without breaking your client’s code.

Solution to Question 6–4.

Encapsulation is the principle of keeping each class discreet and self-contained, so you can change the implementation of one class without affecting any other class.

Solution to Question 6–5.

Specialization allows a new class to “inherit” many of the characteristics of an existing class, and to be used polymorphically with that class. Specialization is implemented in C# through inheritance.

Solution to Question 6–6.

Polymorphism is the ability to treat derived classes as if they were all instances of their base class, yet have each derived class specialize its own implementation of the base class’s methods.

Solution to Question 6–7.

The is-a relationship is established through inheritance. The has-a relationship is implemented through aggregation (making one type a member variable of another type).

Solution to Question 6–8.

Access modifiers indicate which class’ methods have access to a given field, property or method of a class. Public members are available to methods of any class; private members are available only to methods of instances of the same class.

Solution to Question 6–9.

State is the current conditions and values of an object, and is implemented with properties and member variables. Capabilities are what the object can do, exposed through public methods. Responsibilities are the promises a well-designed class makes to the clients of that class.

Solution to Question 6–10.

A use-case scenario is a tool for the analysis of a problem. In a use-case scenario, you walk through the details of how your product will be used by one user to accomplish one task, noting which classes interact and what their responsibilities are.

Chapter 7: Classes and Objects

Quiz

Solution to Question 7–1.

A class defines a new type; an object is a single instance of that type.

Solution to Question 7–2.

Instances of classes are reference types and are created on the heap.

Solution to Question 7–3.

Intrinsic types (such as integers) and structs are value types and are created on the stack.

Solution to Question 7–4.

Access is limited to methods of the defining class.

Solution to Question 7–5.

Access is available to methods in any class.

Solution to Question 7–6.

The class’s constructor is called.

Solution to Question 7–7.

A default constructor is a constructor that takes no parameters. If you do not create any constructor at all for your class, a default constructor is implicitly created.

Solution to Question 7–8.

None. A constructor is not defined to return a type, and is not marked void.

Solution to Question 7–9.

Either in the constructor, using assignment, or when the member variable is created:

private int myVariable = 88;

Technically, only the latter is truly initialization; assigning it in the constructor is not as efficient.

Solution to Question 7–10.

this refers to the object itself—the current instance of the class.

Solution to Question 7–11.

A static method has no this reference. It does not belong to an instance; it belongs to the class and can only call other static methods.

You access a static method through the name of the class:

Dog myDog = new Dog(  );
myDog.InstanceMethod(  );
Dog.StaticMethod(  );

Of course, from within any method (including static methods), you can instantiate a class, and then call methods on that instance.

You can even instantiate an instance of your own class, and then call any non-static method of that object, as we did with [static] Main( ) calling [non-static] Test( ).

Solution to Question 7–12.

The using statement automatically calls the dispose method on the object once the statement completes.

Solution to Exercise 7-1.

Write a program with a Math class that has four methods: Add, Subtract, Multiply, and Divide, each of which takes two parameters. Call each method from Main( ).

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication2
{
   class Math
   {
      public int Add( int left, int right )
      {
         return left + right;
      }

      public int Subtract( int left, int right )
      {
         return left - right;
      }

      public int Multiply( int left, int right )
      {
         return left * right;
      }

      public float Divide( float left, float right )
      {
         return left / right;
      }
   }     // end class Math


   class Program
   {
      static void Main( string[] args )
      {
         Math m = new Math(  );
         int sum =         m.Add(3,5);
         int difference =  m.Subtract(3,5);
         int product =     m.Multiply(3,5);
         float quotient =  m.Divide(3.0f, 5.0f);

         Console.WriteLine(
            "sum: {0}, difference: {1}, product: {2}, quotient: {3}",
            sum, difference, product, quotient);

      }
   }
}
Solution to Exercise 7-2.

Modify the program from Exercise 7-1 so that you do not have to create an instance of Math to call the four methods:

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication2
{
   class Math
   {
      static public int Add( int left, int right )
      {
         return left + right;
      }

      static public int Subtract( int left, int right )
      {
         return left - right;
      }

      static public int Multiply( int left, int right )
      {
         return left * right;
      }

      static public float Divide( float left, float right )
      {
         return left / right;
      }
   }     // end class Math


   class Program
   {
      static void Main( string[] args )
      {

         int sum =         Math.Add( 3, 5 );
         int difference =  Math.Subtract(3,5);
         int product =     Math.Multiply(3,5);
         float quotient =  Math.Divide(3.0f, 5.0f);

         Console.WriteLine(
            "sum: {0}, difference: {1}, product: {2}, quotient: {3}",
            sum, difference, product, quotient);

      }
   }
}

Chapter 8: Inside Methods

Quiz

Solution to Question 8–1.

Method overloading allows the author of the class to create a method with varying (number and/or type of) parameters, rather than having to have many methods with similar but different names.

Solution to Question 8–2.

The signature of a method is its name and its parameter list.

Solution to Question 8–3.

Properties are public accessors to your encapsulated data. Properties appear to the class creator as methods, but to the class’s clients as fields.

Solution to Question 8–4.

Do not implement the set part of the property. No special notation is required.

Solution to Question 8–5.

By passing in parameters by reference and getting the results back in those parameters.

Solution to Question 8–6.

If you want to pass a value object (variable) by reference, you can use the keyword ref in the call to the method and in the declaration of the method.

Solution to Question 8–7.

If you want to pass a value object by reference, but do not want to initialize it, you must use the keyword out in the call to the method and in the declaration of the method.

Exercises

Solution to Exercise 8-1.

Write a program with an overloaded method for doubling the value of the argument. One version of the method should double an int value, and the other version should double a float value. Call both methods to demonstrate that they work.

using System;

namespace InsideMethods
{
   class Tester
   {
      public void Run(  )
      {
         int x = 5;
         float y = 5.2f;
         Console.WriteLine( "Double {0} = {1}", x, Doubler( x ) );
         Console.WriteLine( "Double {0} = {1}", y, Doubler( y ) );
      }

      static int Doubler( int theVal )
      {
         return theVal * 2;
      }

      static float Doubler( float theVal )
      {
         return theVal * 2.0f;
      }

      static void Main(  )
      {
         Tester t = new Tester(  );
         t.Run(  );
      }

   }
}
Solution to Exercise 8-2.

Write a program with one method that takes an int value, and returns both double and triple that value. You’ll need to use reference parameters.

using System;

namespace InsideMethods
{
   class Tester
   {
      public void Run(  )
      {
         int x = 5;
         int doubleX = 0;
         int tripleX =0;
         DoublerAndTripler( x, ref doubleX, ref tripleX );
         Console.WriteLine( "Double {0} = {1}; triple {2} = {3}",
            x, doubleX, x, tripleX );

      }

      static void DoublerAndTripler(
         int theVal, ref int doubleValue, ref int tripleValue )
      {
         doubleValue = theVal * 2;
         tripleValue = theVal * 3;
      }

      static void Main(  )
      {
         Tester t = new Tester(  );
         t.Run(  );
      }

   }
}
Solution to Exercise 8-3.

Modify the program from Exercise 8-2 so that you don’t need to initialize the variables that will hold the doubled and tripled values before calling the method:

using System;

namespace InsideMethods
{
   class Tester
   {
      public void Run(  )
      {
         int x = 5;
         int doubleX;      // uninitialized
         int tripleX;      // uninitialized
         DoublerAndTripler( x, out doubleX, out tripleX );
         Console.WriteLine( "Double {0} = {1}; triple {2} = {3}",
            x, doubleX, x, tripleX );

      }

      static void DoublerAndTripler(
         int theVal, out int doubleValue, out int tripleValue )
      {
         doubleValue = theVal * 2;
         tripleValue = theVal * 3;
      }

      static void Main(  )
      {
         Tester t = new Tester(  );
         t.Run(  );
      }

   }
}

Chapter 9: Basic Debugging

Quiz

Solution to Question 9–1.

Go to the line where you want execution to stop, and click in the margin. A red dot will appear on the line.

Solution to Question 9–2.

Pressing F10 steps over a method; F11 steps into the method.

Solution to Question 9–3.

Right-click on the line and choose breakpoints and then disable breakpoint, or right-click on the breakpoint and choose disable.

Solution to Question 9–4.

The Locals window shows all the variables that are in scope. The Autos window shows variables used in the current and previous statement.

Solution to Question 9–5.

Either right-click on the variable and choose “Add to Watch window” or just click and drag the variable directly onto the Watch window.

Solution to Question 9–6.

Right-click on the variable and choose “QuickWatch,” or select Debug → QuickWatch.

Solution to Question 9–7.

The call stack shows which method called the current method, and which method called that method, etc. This allows you to determine the exact path your code followed to bring you to the current method.

Exercises

Solution to Exercise 9-1.

You’ll use the following program for this exercise. Either type it into Visual Studio, or copy it from this book’s web site. Note that this is spaghetti code—you’d never write method calls like this, but that’s why this is the debugging chapter.

using System;

namespace Debugging
{

    class Tester
    {
        public void Run(  )
        {

            int myInt = 42;
            float myFloat = 9.685f;

            System.Console.WriteLine("Before staring: \n value of myInt: {0} \n
            value of myFloat: {1}", myInt, myFloat);

            // pass the variables by reference
            Multiply( ref myInt, ref myFloat );

            System.Console.WriteLine("After finishing: \n value of myInt: {0} \n
            value of myFloat: {1}", myInt, myFloat);

        }

        private static void Multiply (ref int theInt, ref float theFloat)
        {
            theInt = theInt * 2;
            theFloat = theFloat *2;

            Divide( ref theInt, ref theFloat);
        }

        private static void Divide (ref int theInt, ref float theFloat)
        {
            theInt = theInt / 3;
            theFloat = theFloat / 3;

            Add(ref theInt, ref theFloat);
        }

        public static void Add(ref int theInt, ref float theFloat)
        {
            theInt = theInt + theInt;
            theFloat = theFloat + theFloat;
        }

        static void Main(  )
        {
            Tester t = new Tester(  );
            t.Run(  );
        }
    }
}

Answers:

Solution to Exercise 9-2.

The program in this exercise is similar to the first, but it has a logic error. Type this program into Visual Studio or download it from this book’s web site.

using System;

namespace Debugging
{

    class Tester
    {
        public void Run(  )
        {

            int myInt = 42;
            float myFloat = 9.685f;

            System.Console.WriteLine("Before staring: \n value of myInt: {0} \n
            value of myFloat: {1}", myInt, myFloat);

            // pass the variables by reference
            Multiply( ref myInt, ref myFloat );

            System.Console.WriteLine("After finishing: \n value of myInt: {0} \n
            value of myFloat: {1}", myInt, myFloat);

        }

        private static void Multiply (ref int theInt, ref float theFloat)
        {
            theInt = theInt * 2;
            theFloat = theFloat *2;
            Divide( ref theInt, ref theFloat);
        }

        private static void Divide (ref int theInt, ref float theFloat)
        {
            theInt = theInt * 3;
            theFloat = theFloat * 3;
            Add(ref theInt, ref theFloat);
        }

        public static void Add(ref int theInt, ref float theFloat)
        {
            theInt = theInt - theInt;
            theFloat = theFloat - theFloat;
        }

        static void Main(  )
        {
            Tester t = new Tester(  );
            t.Run(  );
        }
    }
}

If you run this program, you will not get the same results as you did in the previous example. Use the debugging tools you just learned about to find the error. Correct the error, and then run the program again to see if the results are correct.

You could find this error by setting a breakpoint on the call to Run( ), and stepping through the code from there, watching the values of theInt and theFloat. You could also find it by setting breakpoints on each of the method calls and examining the values of theInt and theFloat each time.

The first errors you’ll probably find are these in Divide( ):

theInt = theInt * 3;
theFloat = theFloat * 3;

theInt and theFloat are multiplied by 3, not divided. However, if you fix these errors and run the program, the result is still 0 for both variables. That’s because there are two more errors in Add( ):

theInt = theInt - theInt;
theFloat = theFloat - theFloat;

As you can see, the programmer isn’t a very good typist—the variables are subtracted instead of added. If you fix these errors, the program will run as expected.

Chapter 10: Arrays

Quiz

Solution to Question 10–1.

6. Arrays always begin with index (or offset) zero.

Solution to Question 10–2.

Yes. Every array declares the type of objects it will hold. You can undermine the type-safety by creating an array of objects (which will hold anything, because everything derives from objects), but that is not advised.

Solution to Question 10–3.

Arrays are reference types and created on the heap.

Solution to Question 10–4.

If the elements of the array are of value types, they are created in the allocated memory for the array; otherwise, they are created elsewhere on the heap and a reference is stored in the array.

Solution to Question 10–5.

You can explicitly call new or just imply the size of the array. For example, if you have three employee objects named moe, larry, and curley:

Employee[] myEmpArray = new Employee[3] = { moe, larry, curley };

or:

Employee[] myEmpArray = { moe, larry, curley };
Solution to Question 10–6.

Allows you to pass in an indefinite number of parameters, all of the same type, which will be treated as an array. You can, if you wish, also pass in an array.

Solution to Question 10–7.

A rectangular array is a multidimensional array; a jagged array is an array of arrays.

Solution to Exercise 10-1.

Declare a Dog class with two private members: weight (an int), and name (a string). Be sure to add properties to access the members. Then create an array that holds three Dog objects (Milo, 26 pounds; Frisky, 10 pounds; and Laika, 50 pounds). Output each dog’s name and weight.

using System;
namespace Exercises
{
    public class Dog
    {

      public Dog(int theWeight, string theName)
      {
          this.weight = theWeight;
          this.name = theName;
      }

      public int Weight
      {
          get
          {
              return weight;
          }
          set
          {
              weight = value;
          }
      }

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


      private int weight;
      private string name;
   }
   public class Tester
   {
      public void Run(  )
      {
         Dog milo = new Dog(26, "Milo");
         Dog frisky = new Dog(10, "Frisky");
         Dog laika = new Dog(50, "Laika");

         Dog[] dogArray = {milo, frisky, laika};


         // output array values

         foreach (Dog d in dogArray)
         {
             Console.WriteLine("Dog {0} weighs {1} pounds.", d.Name, d.Weight);
         }

      }
       static void Main(  )
       {
           Tester t = new Tester(  );
           t.Run(  );
       }
   }
}
Solution to Exercise 10-2.

Create an array of 10 integers. Populate the array by having the user enter integers at the console (use Console.Readline). Don’t worry about error checking for this exercise. Output the integers sorted from greatest to least.

using System;
namespace Exercises
{

   public class Tester
   {
      public void Run(  )
      {
         int[] intArray = new int[10];

         Console.WriteLine("You'll be asked to enter 10 integers");

         // enter data into the array
         for (int i = 0; i < intArray.Length; i++ )
         {
             Console.Write("Enter an integer: ");
             string theEntry = Console.ReadLine(  );
             intArray[i] = Convert.ToInt32(theEntry);
         }

         // sort and reverse the array
         Array.Sort(intArray);
         Array.Reverse(intArray);

         Console.WriteLine("\nValues:");
         foreach (int j in intArray)
         {
             Console.WriteLine("{0}", j);
         }

      }
       static void Main(  )
       {
           Tester t = new Tester(  );
           t.Run(  );
       }
   }
}
Solution to Exercise 10-3.

Extend Exercise 10-1 by creating a two-dimensional array that represents a collection of strings that indicate the awards each dog has won at dog shows. Each dog may have a different number of awards won. Output the contents of the array to check its validity.

using System;
namespace exercise
{
    public class Dog
    {

        public Dog(int theWeight, string theName)
        {
            this.weight = theWeight;
            this.name = theName;
        }

        public int Weight
        {
            get
            {
                return weight;
            }
            set
            {
                weight = value;
            }
        }

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


        private int weight;
        private string name;
    }
    public class Tester
    {
        public void Run(  )
        {
            const int rows = 3;

            // declare and populate the dogs array
            Dog milo = new Dog(26, "Milo");
            Dog frisky = new Dog(10, "Frisky");
            Dog laika = new Dog(50, "Laika");

            Dog[] dogArray = {milo, frisky, laika};


            // declare the dogAwards array as 3 rows high
            string[][] dogAwardsArray = new string[rows][];

            // declare the rows
            dogAwardsArray[0] = new string[3];
            dogAwardsArray[1] = new string[1];
            dogAwardsArray[2] = new string[2];

            // Populate the rows
            dogAwardsArray[0][0] = "Best in Show";
            dogAwardsArray[0][1] = "Best of Breed";
            dogAwardsArray[0][2] = "Judge's Cup";
            dogAwardsArray[1][0] = "Best Toy Tog";
            dogAwardsArray[2][0] = "Best Working Dog";
            dogAwardsArray[2][1] = "Best Large Dog";

            // Output the contents
            for (int i = 0; i < dogAwardsArray.Length; i++)
            {
                Console.WriteLine("{0}'s awards: ", dogArray[i].Name);
                for (int j = 0; j < dogAwardsArray[i].Length; j++)
                {
                    Console.WriteLine("\t{0}", dogAwardsArray[i][j]);
                }
            }

        }
       static void Main(  )
       {
           Tester t = new Tester(  );
           t.Run(  );
       }
    }
}
Solution to Exercise 10-4.

Create a two-dimensional array that represents a chessboard (an 8 × 8 array). Each element in the array should contain either the string “black” or “white,” depending on where it is on the board. Create a method that initializes the array with the strings. Then create a method that asks the reader to enter two integers for the coordinates of a square, and returns whether that square is black or white.

using System;

namespace exercises
{
    public class Tester
    {
        public void Run(  )
        {
            const int rows = 8;
            const int columns = 8;

            // create an 8x8 array
            string[,] chessboardArray = new string[rows, columns];

            // populate the chessboard array

            for (int i = 0; i < rows; i++)
            {
                // if row starts with a black square
                if ((i % 2) == 0)
                {
                    for (int j = 0; j < columns; j++)
                    {
                        if ((j % 2) == 0)
                        {
                            chessboardArray[i,j] = "black";
                        }
                        else
                        {
                            chessboardArray[i,j] = "white";
                        }
                    }
                }
                // else row starts with a white square
                else
                {
                    for (int j = 0; j < columns; j++)
                    {
                        if ((j % 2) == 0)
                        {
                            chessboardArray[i,j] = "white";
                        }
                        else
                        {
                            chessboardArray[i,j] = "black";
                        }
                    }
                }
             }

          // ask the user for coordinates to test
            Console.Write("Enter the row to test (1 through 8): ");
            string rowEntry = Console.ReadLine(  );
            int testRow = Convert.ToInt32(rowEntry);
            Console.Write("Enter the column to test (1 through 8): ");
            string colEntry = Console.ReadLine(  );
            int testCol = Convert.ToInt32(colEntry);

            // output the value at those coordinates
            Console.WriteLine("The square at {0}, {1} is {2}.", testRow,
            testCol, chessboardArray[(testRow - 1), (testCol - 1)]);
        }

        static void Main(  )
       {
           Tester t = new Tester(  );
           t.Run(  );
       }
    }
}

Chapter 11: Inheritance and Polymorphism

Quiz

Solution to Question 11–1.

This relationship is reciprocal: if you have three types with similar functionality, you can refactor that similarity out of the three types into a generalized type. At the same time, each of the three types are now specialized forms of the more generalized type.

Inheritance is also implicitly hierarchical: you can imagine a tree with the most generalized type at the top and each level of specialization descending from levels above. A generalized type may have many specializations, but each specialized type may have only one generalization.

Solution to Question 11–2.

Through inheritance.

Solution to Question 11–3.

class <identifier> : <base class>

Solution to Question 11–4.

Create a virtual method in the base class, and then override it in the derived class.

Solution to Question 11–5.

The more usual meaning is to allocate memory on the heap. The special meaning in inheritance is that you are not overriding a base method; you are creating a new method that intentionally hides and replaces the base class method.

Solution to Question 11–6.

After the parameter list, but before the opening brace, put a colon followed by the keyword base and two parentheses. Pass the parameters for the base class constructor within the parentheses.

Solution to Question 11–7.

A member of class A is visible to class B if it is marked public, or if it is marked protected and class B derives directly or indirectly from A. If the member is marked private in A, it is always invisible to class B.

Solution to Question 11–8.

An abstract method has no implementation in the base class, but must be overridden and implemented in any derived class that does not itself want to be abstract. Any class with an abstract method (even if inherited) is itself abstract and may not be instantiated.

Solution to Question 11–9.

A sealed class is one that the compiler will not let you derive from. Classes are marked sealed when the designer of the class does not want anyone to create a derived version.

Solution to Question 11–10.

Object (which is the ultimate base class (root) of all types in C#).

Solution to Question 11–11.

Object.

Solution to Question 11–12.

If you use a value type where an Object is expected, the value type will be automatically and invisibly wrapped in an Object and will take on reference characteristics.

Solution to Question 11–13.

When you return a boxed type back to its original type, you must unbox it and explicitly cast it.

Exercises

Solution to Exercise 11-1.

Create a base class, Telephone, and derive a class Electronic-Phone from it. In Telephone, create a protected string member phonetype, and a public method Ring( ) that outputs a text message such as: “Ringing the <phonetype>.” In ElectronicPhone, the constructor should set the phonetype to “Digital.” In the Run( ) method, call Ring( ) on the ElectronicPhone to test the inheritance.

using System;
public class Tester
{

   public class Telephone
   {
      protected string phonetype;
      public void Ring(  )
      {
         Console.WriteLine( "Ringing the {0} phone...", phonetype );
      }
   }

   public class ElectronicPhone : Telephone
   {
      public ElectronicPhone(  )
      {
         this.phonetype = "Digital";   // access protected member
      }
   }

   public void Run(  )
   {
      ElectronicPhone phone = new ElectronicPhone(  );
      phone.Ring(  ); // accessing the base method
   }

   static void Main(  )
   {
      Tester t = new Tester(  );
      t.Run(  );
   }
}
Solution to Exercise 11-2.

Extend Exercise 11-1 to illustrate a polymorphic method. Have the derived class override the Ring( ) method to display a different message.

using System;
public class Tester
{

   public class Telephone
   {
      protected string phonetype;
      public virtual void Ring(  )  // now virtual
      {
         Console.WriteLine( "Ringing the {0} phone. Ring Ring.", phonetype );
      }
   }

   public class ElectronicPhone : Telephone
   {
      public ElectronicPhone(  )
      {
         this.phonetype = "Digital";   // access protected member
      }
      public override void Ring(  )      // override
      {
         Console.WriteLine( "Ringing the {0} phone. Beep Beep.", phonetype );
      }
   }

   public void Run(  )
   {
      // assign derived instance to base reference
      Telephone phone = new ElectronicPhone(  );
      phone.Ring(  ); // accessing the polymorphic method
   }

   static void Main(  )
   {
      Tester t = new Tester(  );
      t.Run(  );
   }
}
Solution to Exercise 11-3.

Change the Telephone class to abstract, and make Ring( ) an abstract method. Derive two new classes from Telephone: DigitalPhone and Talking-Phone. Each derived class should set the phonetype, and override the Ring( ) method.

using System;
public class Tester
{

   public abstract class Telephone
   {
      protected string phonetype;
      public abstract void Ring(  ); // now abstract
   }

   public class DigitalPhone : Telephone
   {
      public DigitalPhone(  )
      {
         this.phonetype = "Digital";   // access protected member
      }
      public override void Ring(  )      // implement
      {
         Console.WriteLine( "Ringing the {0} phone. Beep Beep.", phonetype );
      }
   }

   public class TalkingPhone : Telephone
   {
      public TalkingPhone(  )
      {
         this.phonetype = "Talking";   // access protected member
      }
      public override void Ring(  )      // implement
      {
         Console.WriteLine( "Ringing the {0} phone. You have a call.",
                            phonetype );
      }
   }


   public void Run(  )
   {
      // assign derived instance to base reference
      Telephone phone1 = new DigitalPhone(  );
      Telephone phone2 = new TalkingPhone(  );
      phone1.Ring(  ); // accessing the polymorphic method
      phone2.Ring(  ); // accessing the polymorphic method

   }

   static void Main(  )
   {
      Tester t = new Tester(  );
      t.Run(  );
   }
}

Chapter 12: Operator Overloading

Quiz

Solution to Question 12–1.

The process of writing methods for your class that allow clients of your class to interact with your class using standard operators (+, ==).

Solution to Question 12–2.

Static methods.

Solution to Question 12–3.

Call to:

public static Fraction operator+(f2, f1)
Solution to Question 12–4.

Overload the Equals( ) method.

Solution to Question 12–5.

Use implicit conversion when you know the conversion will succeed without the risk of losing information. Use explicit conversion if information might be lost.

Solution to Exercise 12-1.

Create a class Invoice, that has a string property vendor and a double property amount. Overload the addition operator so that if the vendor properties match, the amount properties of the two invoices are added together in a new invoice. If the vendor properties do not match, the new invoice is blank.

using System;

public class Invoice
{
    private string vendor;
    private double amount;

    public string Vendor
    {
        get
        {
            return vendor;
        }
        set
        {
            vendor = value;
        }
    }

    public double Amount
    {
        get
        {
            return amount;
        }
        set
        {
            amount = value;
        }
    }

    // constructor
    public Invoice(string vendor, double amount)
    {
        this.vendor = vendor;
        this.amount = amount;
    }

    // Overloaded operator + takes two invoices.
    // If the vendors are the same, the two amounts are added.
    // If not, the operation fails, and a blank invoice is returned.

    public static Invoice operator +(Invoice lhs, Invoice rhs)
    {
        if (lhs.vendor == rhs.vendor)
        {
            return new Invoice(lhs.vendor, lhs.amount + rhs.amount);
        }
        else
        {
            Console.WriteLine("Vendors don't match; operation failed.");
            return new Invoice("", 0);
        }
    }

        public void PrintInvoice()
    {
        Console.WriteLine("Invoice from {0} for ${1}.", this.vendor,
                          this.amount);
    }
}


public class Tester
{
    public void Run(  )
    {
        Invoice firstInvoice = new Invoice("TinyCorp", 345);
        Invoice secondInvoice = new Invoice("SuperMegaCo", 56389.53);
        Invoice thirdInvoice = new Invoice("SuperMegaCo", 399.65);

        Console.WriteLine("Adding first and second invoices.");
        Invoice addedInvoice = firstInvoice + secondInvoice;
        addedInvoice.PrintInvoice(  );

        Console.WriteLine("Adding second and third invoices.");
        Invoice otherAddedInvoice = secondInvoice + thirdInvoice;
        otherAddedInvoice.PrintInvoice(  );


    }
    static void Main(  )
    {
        Tester t = new Tester(  );
        t.Run(  );
    }
}
Solution to Exercise 12-2.

Modify the Invoice class so that two invoices are considered equal if the vendor and amount properties match. Test your methods.

using System;

public class Invoice
{
    private string vendor;
    private double amount;

    public string Vendor
    {
        get
        {
            return vendor;
        }
        set
        {
            vendor = value;
        }
    }

    public double Amount
    {
        get
        {
            return amount;
        }
        set
        {
            amount = value;
        }
    }

    // constructor
    public Invoice(string vendor, double amount)
    {
        this.vendor = vendor;
        this.amount = amount;
    }

    // Overloaded operator + takes two invoices.
    // If the vendors are the same, the two amounts are added.
    // If not, the operation fails, and a blank invoice is returned.

    public static Invoice operator+ (Invoice lhs, Invoice rhs)
    {
        if (lhs.vendor == rhs.vendor)
        {
            return new Invoice(lhs.vendor, lhs.amount + rhs.amount);
        }
        else
        {
            Console.WriteLine("Vendors don't match; operation failed.");
            return new Invoice("", 0);
        }
    }

    // overloaded equality operator
    public static bool operator== (Invoice lhs, Invoice rhs)
    {
        if ( lhs.vendor == rhs.vendor && lhs.amount == rhs.amount )
        {
            return true;
        }
        return false;
    }

    // overloaded inequality operator, delegates to ==
    public static bool operator !=(Invoice lhs, Invoice rhs)
    {
        return !(lhs == rhs);
    }

    // method for determining equality; tests for same type,
    // then delegates to ==
    public override bool Equals(object o)
    {
        if (!(o is Invoice))
        {
            return false;
        }
        return this == (Invoice)o;
    }

    public void PrintInvoice()
    {
        Console.WriteLine("Invoice from {0} for ${1}.", this.vendor,
                          this.amount);
    }
}


public class Tester
{
    public void Run(  )
    {
        Invoice firstInvoice = new Invoice("TinyCorp", 399.65);
        Invoice secondInvoice = new Invoice("SuperMegaCo", 56389.53);
        Invoice thirdInvoice = new Invoice("SuperMegaCo", 399.65);

        Invoice testInvoice = new Invoice("SuperMegaCo", 399.65);

        if (testInvoice == firstInvoice)
        {
            Console.WriteLine("First invoice matches.");
        }
        else if (testInvoice == secondInvoice)
        {
            Console.WriteLine("Second invoice matches.");
        }
        else if (testInvoice == thirdInvoice)
        {
            Console.WriteLine("Third invoice matches.");
        }
        else
        {
            Console.WriteLine("No matching invoices.");
        }

    }
    static void Main(  )
    {
        Tester t = new Tester(  );
        t.Run(  );
    }
}

Chapter 13: Interfaces

Quiz

Solution to Question 13–1.

The interface defines the methods, properties, etc. that the implementing class must provide. The implementing class provides these members and, optionally, additional members.

Solution to Question 13–2.

Every class has exactly one base class, but may implement zero, one, or more interfaces. An abstract base class serves as the base to a derived class that must implement all its abstract methods or that derived class is also abstract.

Solution to Question 13–3.

class MyClass : MyBase, ISuppose, IDo {}

Note that the base class must come first after the colon.

Solution to Question 13–4.

The is and the as operator.

Solution to Question 13–5.

is returns false if the interface is not implemented; as returns null. Using the as operator can be more efficient.

Solution to Question 13–6.

Extending an interface is very much like deriving a class. The new interface inherits all the members of the parent interface, and can also include additional methods.

Solution to Question 13–7.

ExtendedInterface : OriginalInterface

For example, read:

ILoggedCompressible : ICompressible

as “ILoggb[edCompressible extends ICompressible.”

Solution to Question 13–8.

The class implementing a method of the interface can mark that method virtual, and the implementation of the method can be overridden in derived classes.

Solution to Question 13–9.

Explicit interface implementation identifies the member of the interface by naming the interface itself (e.g., IStorable.Write( )). This is done to differentiate implementation methods when there might otherwise be an ambiguity, such as when implementing more than one interface that have methods with the same signature.

Solution to Exercise 13-1.

Define an interface IConvertible that indicates that the class can convert a string to C# or VB2005. The interface should have two methods: ConvertToCSharp and ConvertToVB2005. Each method should take a string, and return a string.

using System;

namespace Exercises
{
   interface IConvertible
   {
      string ConvertToCSharp( string stringToConvert );
      string ConvertToVB2005( string stringToConvert );
   }
}
Solution to Exercise 13-2.

Implement that interface and test it by creating a class ProgramHelper that implements IConvertible. You can use simple string messages to simulate the conversion.

using System;

namespace Exercises
{
   interface IConvertible
   {
      string ConvertToCSharp( string stringToConvert );
      string ConvertToVB2005( string stringToConvert );
   }


   public class ProgramHelper : IConvertible
   {
      public ProgramHelper( )
      {
         Console.WriteLine( "Creating ProgramHelper" );
      }

      public virtual string ConvertToCSharp( string stringToConvert )
      {
         return "Converting the string you passed in to CSharp syntax";
      }

      public virtual string ConvertToVB2005( string stringToConvert )
      {
         return "Converting the string you passed in to VB 2005 syntax";
      }

   }  // end class ProgramHelper

   class Tester
   {

      static void Main(  )
      {
         Tester t = new Tester(  );
         t.Run(  );
      }

      public void Run(  )
      {
         // Create a ProgramHelper object
         ProgramHelper theProgramHelper = new ProgramHelper(  );

         // convert a line of CSharp to vb
         string vbString = (
             "Public Sub Read(  ) Implements IStorable.Read " +
            "Console.WriteLine(\"Implementing the Read Method for IStorable\")"
            + "End Sub 'Read\")" );

         Console.WriteLine( vbString );

         // convert the converted line back to CSharp
         string cSharpString = theProgramHelper.ConvertToCSharp( vbString );

         Console.WriteLine( cSharpString );

      }  // end run
   }     // end class Tester
}        // end namespace
Solution to Exercise 13-3.

Extend the interface by creating a new interface, IConvertible. The new interface should implement one new method, CodeCheckSyntax, which takes two strings: the string to check, and the language to use. The method should return a bool. Revise the ProgramHelper class from Exercise 13-2 to use the new interface.

using System;

namespace Exercises
{
   interface IConvertible
   {
      string ConvertToCSharp( string stringToConvert );
      string ConvertToVB2005( string stringToConvert );
   }

   interface ICodeChecker : IConvertible
   {
      bool CheckCodeSyntax( string stringToCheck, string whichLang );
   }


   public class ProgramHelper : ICodeChecker
   {
      public ProgramHelper( )
      {
         Console.WriteLine( "Creating ProgramHelper" );
      }

      public virtual string ConvertToCSharp( string stringToConvert )
      {
         return "Converting the string you passed in to CSharp syntax";
      }

      public virtual string ConvertToVB2005( string stringToConvert )
      {
         return "Converting the string you passed in to VB 2005 syntax";
      }

      public bool CheckCodeSyntax ( string stringToCheck, string whichLang)
      {
         switch ( whichLang )
         {
            case "CSharp":
               Console.WriteLine(
                   "Checking the string \"{0}\" for C# Syntax", stringToCheck );
               return true;
            case "VB2005":
               Console.WriteLine(
                     "Checking the string \"{0}\" for VB 2005 Syntax",
                      stringToCheck );
               return true;
            default:
               return false;
         }

      }

   }  // end class ProgramHelper

   class Tester
   {

      static void Main(  )
      {
         Tester t = new Tester(  );
         t.Run(  );
      }

      public void Run(  )
      {
         // Create a ProgramHelper object
         ProgramHelper theProgramHelper = new ProgramHelper(  );

         // convert a line of CSharp to VB
         string cSharpString = theProgramHelper.ConvertToCSharp(
            "Public Sub Read(  ) Implements IStorable.Read " +
            "Console.WriteLine(\"Implementing the Read Method for IStorable\")"
            + "End Sub 'Read\")" );

         Console.WriteLine( cSharpString );
         Console.WriteLine (
            "Checking \"{0}\" for syntax... Result {1}", cSharpString,
             theProgramHelper.CheckCodeSyntax(cSharpString, "CSharp"));

         // convert the converted line back to VB
         string vbString = theProgramHelper.ConvertToCSharp( cSharpString );

         Console.WriteLine( vbString );
         Console.WriteLine (
          "Checking \"{0}\" for syntax... Result {1}", vbString,
           theProgramHelper.CheckCodeSyntax(vbString,"VB2005"));

      }  // end run
   }     // end class Tester
}        // end namespace
Solution to Exercise 13-4.

Demonstrate the use of is and as. Create a new class, ProgramConverter, that implements IConvertible. ProgramConverter should implement the ConvertToCSharp( ) and ConvertToVB( ) methods. Revise ProgramHelper so that it derives from ProgramConverter, and implements ICodeChecker.

using System;

namespace Exercises
{
   interface IConvertible
   {
      string ConvertToCSharp( string stringToConvert );
      string ConvertToVB2005( string stringToConvert );
   }

   interface ICodeChecker : IConvertible
   {
      bool CheckCodeSyntax( string stringToCheck, string whichLang );
   }

   public class ProgramConverter : IConvertible
   {
      public ProgramConverter(  )
      {
         Console.WriteLine( "Creating ProgramConverter" );
      }
      public virtual string ConvertToCSharp( string stringToConvert )
      {
         return "Converting the string you passed in to CSharp syntax";
      }
      public virtual string ConvertToVB2005( string stringToConvert )
      {
         return "Converting the string you passed in to VB 2005 syntax";
      }

   }

   public class ProgramHelper : ProgramConverter, ICodeChecker
   {
      public ProgramHelper(  )
      {
         Console.WriteLine( "Creating ProgramHelper" );
      }

      public bool CheckCodeSyntax( string stringToCheck, string whichLang )
      {
         switch ( whichLang )
         {
            case "CSharp":
               Console.WriteLine(
                 "Checking the string {0} for C# Syntax", stringToCheck );
               return true;
            case "VB2005":
               Console.WriteLine(
                  "Checking the string {0} for VB 2005 Syntax", stringToCheck );
               return true;
            default:
               return false;
         }     // end switch
      }        // end method Check Code Syntax
   }           // end class ProgramHelper

   class Tester
   {

      static void Main(  )
      {
         Tester t = new Tester(  );
         t.Run(  );
      }

      public void Run(  )
      {
         ProgramConverter[] converters = new ProgramConverter[4];
         converters[0] = new ProgramConverter(  );
         converters[1] = new ProgramHelper(  );
         converters[2] = new ProgramHelper(  );
         converters[3] = new ProgramConverter(  );

         foreach ( ProgramConverter pc in converters )
         {
            string vbString =
              pc.ConvertToCSharp( "Public Sub Read() Implements IStorable.Read "
              + "Console.WriteLine(\"Implementing the Read Method for
                                   IStorable\")"
              + "End Sub 'Read\")" );

            Console.WriteLine( vbString );

            ProgramHelper ph = pc as ProgramHelper;
            if ( ph != null )
            {
               ph.CheckCodeSyntax( vbString, "VB2005" );
            }
            else
            {
               Console.WriteLine( "No vb syntax check - not a Program helper" );
            }

            string cSharpString = pc.ConvertToCSharp( vbString );
            Console.WriteLine( cSharpString );
            if ( ph != null )
            {
               ph.CheckCodeSyntax( vbString, "CSharp" );
            }
            else
            {
               Console.WriteLine(
                  "No csharp syntax check - not a Program helper" );
            }
         }  // end foreach in converters
      }  // end run
   }     // end class Tester
}        // end namespace

Chapter 14: Generics and Collections

Solution to Exercise 14-1.

Create an abstract Animal class that has private members weight and name, and abstract methods Speak( ), Move( ), and ToString( ). Derive from Animal a Cat and Dog class that override the methods appropriately. Create an Animal array, populate it with Dogs and Cats, and then call each member’s overridden virtual method.

using System;

abstract public class Animal
{
   protected int weight;
   protected string name;
   public Animal(int weight, string name)
   {
      this.weight = weight;
      this.name = name;
   }
   abstract public  void Speak(  );
   abstract public  void Move(  );
   public override string  ToString(  )
   {
      throw new Exception("The method or operation is not implemented.");
   }
}

public class Dog : Animal
{
   private string breed;

   public string Breed
   {
      get { return breed; }
      set { breed = value; }
   }
   public Dog (int weight, string name, string breed):
   base(weight,name)
   {
      this.breed = breed;
   }

   public override void Speak(  )
   {  Console.WriteLine ("Woof"); }

   public override void Move (  )
   { Console.WriteLine ("Run, run, run, drool.");  }

   public override string  ToString(  )
   {
      return "My name is " + this.name + " I weigh " + this.weight +
      " and I am a " + this.breed + "\n";
   }
}



public class Cat : Animal
{

   public Cat (int weight, string name):
   base(weight,name)
   {}

   public override void Speak(  )
   {  Console.WriteLine ("Meow"); }

   public override void Move (  )
   { Console.WriteLine ("Run, tumble, nap.");  }

   public override string ToString(  )
   {
      return "My name is " + this.name + " I weigh " + this.weight +
      " and I know how to purr!\n";
   }

   public void Purr(  )
   {
      Console.WriteLine("Purrrrrrrrrrrrrrrrrrrrrrrrrr\n");
   }

}

public class Tester
{
   static void Main(  )
   {
      Animal[] myAnimals = new Animal[5];
      myAnimals[0] = new Dog( 72, "Milo", "Golden" );
      myAnimals[1] = new Cat( 12, "Shakespeare");
      myAnimals[2] = new Cat( 10, "Allegra");
      myAnimals[3] = new Dog( 50, "Dingo", "mixed breed" );
      myAnimals[4] = new Dog( 20, "Brandy", "Beagle" );

      foreach ( Animal a in myAnimals )
      {
         a.Speak(  );
         a.Move(  );
         Console.WriteLine(a);

         Cat c = a as Cat;  // cast to cat
         if ( c != null )   // if it is a cat
         {
            c.Purr(  );      // only cats purr
         }
      }
   }

}
Solution to Exercise 14-2.

Replace the array in Exercise 14-1 with a List. Sort the animals by size. You can simplify by just calling ToString( ) before and after the sort. Remember that you’ll need to implement IComparable.

using System;
using System.Collections.Generic;

abstract public class Animal : IComparable
{
   protected int weight;
   protected string name;

   public Animal(int weight, string name)
   {
      this.weight = weight;
      this.name = name;
   }
   abstract public  void Speak(  );
   abstract public  void Move(  );

   public override string  ToString(  )
   {
       throw new Exception("The method or operation is not implemented.");
   }

   public int CompareTo( Object rhs )
   {
      Animal otherAnimal = rhs as Animal;
      if ( otherAnimal != null )
      {
         return this.weight.CompareTo( otherAnimal.weight );
      }
      else
      {
         throw new ApplicationException("Expected to compare animals");
      }
   }
}

public class Dog : Animal
{
   private string breed;

   public string Breed
   {
      get { return breed; }
      set { breed = value; }
   }
   public Dog (int weight, string name, string breed):
   base(weight,name)
   {
      this.breed = breed;
   }

   public override void Speak(  )
   {  Console.WriteLine ("Woof"); }

   public override void Move (  )
   { Console.WriteLine ("Run, run, run, drool.");  }

   public override string  ToString(  )
   {
      return "My name is " + this.name + " I weigh " + this.weight +
      " and I am a " + this.breed ;
   }
}

public class Cat : Animal
{

   public Cat (int weight, string name):
   base(weight,name)
   {}

   public override void Speak(  )
   {  Console.WriteLine ("Meow"); }

   public override void Move (  )
   { Console.WriteLine ("Run, tumble, nap.");  }

   public override string ToString(  )
   {
      return "My name is " + this.name + " I weigh " + this.weight +
      " and I know how to purr!";
   }

   public void Purr(  )
   {
      Console.WriteLine("Purrrrrrrrrrrrrrrrrrrrrrrrrr\n");
   }
}

public class Tester
{
   static void Main(  )
   {
      List<Animal> myAnimals = new List<Animal>(  );
      myAnimals.Add(new Dog( 72, "Milo", "Golden" ));
      myAnimals.Add(new Cat( 12, "Shakespeare"));
      myAnimals.Add(new Cat( 10, "Allegra"));
      myAnimals.Add(new Dog( 50, "Dingo", "mixed breed" ));
      myAnimals.Add(new Dog( 20, "Brandy", "Beagle" ));

      foreach ( Animal a in myAnimals )
      {
         Console.WriteLine(a);
      }

      Console.WriteLine( "\nAfter sorting by size..." );
      myAnimals.Sort(  );
      foreach ( Animal a in myAnimals )
      {
         Console.WriteLine( a );
      }
   }
}
Solution to Exercise 14-3.

Replace the list from Exercise 14-2 with both a Stack and a Queue, and see the difference in the order in which the animals are returned.

The animal definitions are unchanged; the Tester class has the changes:

Using System.Collections.Generic;

public class Tester
{
   static void Main(  )
   {
      Console.WriteLine(
        "Adding in the order: Milo, Shakespeare, Allegra, Dingo, Brandy" );

      Stack<Animal> myStackOfAnimals = new Stack<Animal>(  );
      myStackOfAnimals.Push( new Dog( 72, "Milo", "Golden" ) );
      myStackOfAnimals.Push( new Cat( 12, "Shakespeare" ) );
      myStackOfAnimals.Push( new Cat( 10, "Allegra" ) );
      myStackOfAnimals.Push( new Dog( 50, "Dingo", "mixed breed" ) );
      myStackOfAnimals.Push( new Dog( 20, "Brandy", "Beagle" ) );

      Queue<Animal> myQueueOfAnimals = new Queue<Animal>(  );
      myQueueOfAnimals.Enqueue( new Dog( 72, "Milo", "Golden" ) );
      myQueueOfAnimals.Enqueue( new Cat( 12, "Shakespeare" ) );
      myQueueOfAnimals.Enqueue( new Cat( 10, "Allegra" ) );
      myQueueOfAnimals.Enqueue( new Dog( 50, "Dingo", "mixed breed" ) );
      myQueueOfAnimals.Enqueue( new Dog( 20, "Brandy", "Beagle" ) );


      Console.WriteLine( "The stack..." );
      foreach ( Animal a in myStackOfAnimals )
      {
         Console.WriteLine(a);
      }

      Console.WriteLine( "The queue..." );
      foreach ( Animal a in myQueueOfAnimals )
      {
         Console.WriteLine( a );
      }
   }
}
Solution to Exercise 14-4.

Rewrite Exercise 14-2 to allow Animals to be sorted either by weight or alphabetically by name:

using System;
using System.Collections.Generic;

// simplified to show comparision
abstract public class Animal : IComparable<Animal>
{
   protected int weight;
   protected string name;

   public Animal( int weight, string name )
   {
      this.weight = weight;
      this.name = name;
   }

   // ** new **
   public static AnimalComparer GetComparer()
   {
      return new Animal.AnimalComparer();
   }

   public int CompareTo( Animal rhs )
   {
      return this.weight.CompareTo( rhs.weight );
   }

   // ** new **
   public int CompareTo( Animal rhs,
      Animal.AnimalComparer.ComparisonType whichComparison )
   {
      switch ( whichComparison )
      {
         case AnimalComparer.ComparisonType.name:
            return this.name.CompareTo( rhs.name );
         case AnimalComparer.ComparisonType.size:
            return this.weight.CompareTo( rhs.weight );
      }
      return -1;  // gotta have all paths return a value

   }

   // nested class   ** new **
   public class AnimalComparer : IComparer<Animal>
   {
      // how do you want to compare?
      public enum ComparisonType
      {
         Size,
         Name
      };

      private Animal.AnimalComparer.ComparisonType whichComparison;
      public Animal.AnimalComparer.ComparisonType WhichComparison
      {
         get { return whichComparison; }
         set { whichComparison = value; }
      }

      // compare two Animals using the previously set
      // whichComparison value
      public int Compare( Animal lhs, Animal rhs )
      {
         return lhs.CompareTo( rhs, whichComparison );
      }

      // required to fulfill implementation
      public bool Equals( Animal lhs, Animal rhs )
      {
         return this.Compare( lhs, rhs ) == 0;
      }

      // required to fulfill implementation
      public int GetHashCode( Animal a )
      {
         return a.weight.GetHashCode();
      }
   }     // end nested class
}        // end class Animal


public class Dog : Animal
{
   public Dog( int weight, string name, string breed ) :
   base( weight, name )
   {}

   public override string ToString()
   {
      return "My name is " + this.name + " I weigh " + this.weight;
   }
}

public class Cat : Animal
{

   public Cat( int weight, string name ) :
   base( weight, name )
   { }

   public override string ToString()
   {
      return "My name is " + this.name + " I weigh " + this.weight;
   }
}

public class Tester
{
   static void Main()
   {

      List<Animal> myAnimals = new List<Animal>();
      myAnimals.Add( new Dog( 70, "Milo", "Golden" ) );
      myAnimals.Add( new Cat( 10, "Shakespeare" ) );
      myAnimals.Add( new Cat( 15, "Allegra" ) );
      myAnimals.Add( new Dog( 50, "Dingo", "mixed breed" ) );
      myAnimals.Add( new Dog( 20, "Brandy", "Beagle" ) );

      Console.WriteLine( "Before sorting..." );
      foreach ( Animal a in myAnimals )
      {
         Console.WriteLine( a );
      }

      Console.WriteLine( "\nAfter sorting by default (weight)..." );
      myAnimals.Sort();
      foreach ( Animal a in myAnimals )
      {
         Console.WriteLine( a );
      }

      Console.WriteLine( "\nAfter sorting by name..." );
      Animal.AnimalComparer animalComparer = Animal.GetComparer();
      animalComparer.WhichComparison = Animal.AnimalComparer.ComparisonType.name;
      myAnimals.Sort( animalComparer );
      foreach ( Animal a in myAnimals )
      {
         Console.WriteLine( a );
      }

      Console.WriteLine( "\nAfter sorting explicitly by size..." );
      animalComparer.WhichComparison = Animal.AnimalComparer.ComparisonType.size;
      myAnimals.Sort( animalComparer );
      foreach ( Animal a in myAnimals )
      {
         Console.WriteLine( a );
      }
   }     // end main
}        // end tester

Chapter 15: Strings

Quiz

Solution to Question 15–1.

string (lowercase) is the C# keyword that maps to the .NET Framework String class. They may be used interchangeably.

Solution to Question 15–2.

IComparable

Guarantees that strings can be sorted

ICloneable

Guarantees that you can call the Clone method on a string object and get back a new duplicate string

IConvertible

Allows strings to be converted to other types (such as integers)

IEnumerable

Guarantees that strings can be iterated over in foreach loops

Solution to Question 15–3.

A quoted string, provided by the programmer, such as “Hello.”

Solution to Question 15–4.

An escape character embedded in a string indicates that the character or punctuation that follows is to be treated as an instruction rather than as part of the string. \n indicates a new line. \" indicates that the quote symbol is in the string, not terminating it.

Solution to Question 15–5.

Verbatim strings are taken “as is” and thus do not require escape characters. Where \\ would indicate a single backslash in a normal string, in a verbatim string, it indicates two backslashes.

Solution to Question 15–6.

Strings cannot be changed. When you appear to change a string, what actually happens is that a new string is created and the old string is destroyed by the garbage collector if it is no longer referenced.

Solution to Question 15–7.

It is not possible to derive from the String class (or any other sealed class).

Solution to Question 15–8.

You can call the Concat method of the String class, but it is more common to use the overloaded + operator.

Solution to Question 15–9.

Given an array of delimiters, Split( ) returns the substrings of the original string.

Solution to Question 15–10.

StringBuilder objects are mutable. When the StringBuilder has the complete set of characters you want, you call ToString( ) to get back a string object.

Solution to Question 15–11.

Regular expressions constitute a language for identifying and manipulating strings using both literal and metacharacters.

Exercises

Solution to Exercise 15-1.

Create the following six strings:

  • String 1: “Hello”

  • String 2: “World”

  • String 3 (a verbatim string): “Come visit us at http://www.LibertyAssociates.com"

  • String 4: a concatenation of strings 1 and 2

  • String 5: “world”

  • String 6: a copy of string 3

Once you have the strings created, do the following:

  1. Output the length of each string.

  2. Output the third character in each string.

  3. Output whether the character “H” appears in each string.

  4. Output which strings are the same as string 2.

  5. Output which strings are the same as string 2, ignoring case.

using System;

namespace StringManipulation
{
   class Tester
   {
      public void Run(  )
      {
         string s1 = "Hello ";
         string s2 = "World";
         string s3 = @"Come visit us at http://www.LibertyAssociates.com";
         string s4 = s1 + s2;
         string s5 = "world";
         string s6 = string.Copy( s3 );

         Console.WriteLine("Here's how long our strings are...");
         Console.WriteLine( "s1: {0} [{1}]", s1.Length, s1 );
         Console.WriteLine( "s2: {0} [{1}]", s2.Length, s2 );
         Console.WriteLine( "s3: {0} [{1}]", s3.Length, s3 );
         Console.WriteLine( "s4: {0} [{1}]", s4.Length, s4 );
         Console.WriteLine( "s5: {0} [{1}]", s5.Length, s5 );
         Console.WriteLine( "s6: {0} [{1}]", s6.Length, s6 );


         Console.WriteLine( "\nHere's the third character in each string..." );
         Console.WriteLine( "s1: {0} [{1}]", s1[2], s1 );
         Console.WriteLine( "s2: {0} [{1}]", s2[2], s2 );
         Console.WriteLine( "s3: {0} [{1}]", s3[2], s3 );
         Console.WriteLine( "s4: {0} [{1}]", s4[2], s4 );
         Console.WriteLine( "s5: {0} [{1}]", s5[2], s5 );
         Console.WriteLine( "s6: {0} [{1}]", s6[2], s6 );

         Console.WriteLine( "\nIs there an h in the string?" );
         Console.WriteLine( "s1: {0} [{1}]",
              s1.ToUpper(  ).IndexOf( 'H' ) >= 0 ? "yes" : "nope", s1 );
         Console.WriteLine( "s2: {0} [{1}]",
              s2.ToUpper(  ).IndexOf( 'H' ) >= 0 ? "yes" : "nope", s2 );
         Console.WriteLine( "s3: {0} [{1}]",
              s3.ToUpper(  ).IndexOf( 'H' ) >= 0 ? "yes" : "nope", s3 );
         Console.WriteLine( "s4: {0} [{1}]",
              s4.ToUpper(  ).IndexOf( 'H' ) >= 0 ? "yes" : "nope", s4 );
         Console.WriteLine( "s5: {0} [{1}]",
              s5.ToUpper(  ).IndexOf( 'H' ) >= 0 ? "yes" : "nope", s5 );
         Console.WriteLine( "s6: {0} [{1}]",
              s6.ToUpper(  ).IndexOf( 'H' ) >= 0 ? "yes" : "nope", s6 );

         Console.WriteLine( "\nWhich strings are the same as s2 [{0}]?", s2 );
         Console.WriteLine( "s1: {0} [{1}]",
              String.Compare( s1, s2 ) == 0 ? "Same!" : "Different", s1 );
         Console.WriteLine( "s2: {0} [{1}]",
              String.Compare( s2, s2 ) == 0 ? "Same!" : "Different", s2 );
         Console.WriteLine( "s3: {0} [{1}]",
              String.Compare( s3, s2 ) == 0 ? "Same!" : "Different", s3 );
         Console.WriteLine( "s4: {0} [{1}]",
              String.Compare( s4, s2 ) == 0 ? "Same!" : "Different", s4 );
         Console.WriteLine( "s5: {0} [{1}]",
              String.Compare( s5, s2 ) == 0 ? "Same!" : "Different", s5 );
         Console.WriteLine( "s6: {0} [{1}]",
              String.Compare( s6, s2 ) == 0 ? "Same!" : "Different", s6 );

         Console.WriteLine(
              "\nWhich strings are the same as s2 [{0}] ignoring case ?", s2 );
         Console.WriteLine( "s1: {0} [{1}]",
              String.Compare( s1, s2, true ) == 0 ? "Same!" : "Different", s1 );
         Console.WriteLine( "s2: {0} [{1}]",
              String.Compare( s2, s2, true ) == 0 ? "Same!" : "Different", s2 );
         Console.WriteLine( "s3: {0} [{1}]",
              String.Compare( s3, s2, true ) == 0 ? "Same!" : "Different", s3 );
         Console.WriteLine( "s4: {0} [{1}]",
              String.Compare( s4, s2, true ) == 0 ? "Same!" : "Different", s4 );
         Console.WriteLine( "s5: {0} [{1}]",
              String.Compare( s5, s2, true ) == 0 ? "Same!" : "Different", s5 );
         Console.WriteLine( "s6: {0} [{1}]",
              String.Compare( s6, s2, true ) == 0 ? "Same!" : "Different", s6 );
     }

      static void Main(  )
      {
         Tester t = new Tester(  );
         t.Run(  );
      }
   }
}
Solution to Exercise 15-2.

Take the following string:

Use a regular expression to split the string into words:

using System;
using System.Text;
using System.Text.RegularExpressions;

namespace RegularExpressions
{
   class Tester
   {
      public void Run(  )
      {
         string importantString =
         "We hold these truths to be self-evident, " +
         "that all men are created equal, " +
         "that they are endowed by their Creator with certain " +
         "unalienable Rights, that among " +
         "these are Life, Liberty and the pursuit of Happiness.";

         Regex theRegex = new Regex( " |, |," );
         StringBuilder sBuilder = new StringBuilder(  );
         int id = 1;

         foreach ( string subString in theRegex.Split( importantString ) )
         {
            sBuilder.AppendFormat(
            "{0}: {1}\n", id++, subString );
         }
         Console.WriteLine( "{0}", sBuilder );
      }

      static void Main(  )
      {
         Tester t = new Tester(  );
         t.Run(  );
      }
   }
}

Chapter 16: Throwing and Catching Exceptions

Quiz

Solution to Question 16–1.

An exception is an object (derived from System.Exception) that contains information about a problematic event. The framework supports throwing exceptions to stop processing and catching events to handle the problem and resume processing.

Solution to Question 16–2.

The stack is unwound until a handler is found, or else the exception is handled by the CLR, which terminates the program.

Solution to Question 16–3.

You create a try/catch block; the catch part is the exception handler.

Solution to Question 16–4.

The syntax is:

throw new Sytem.Arg'umentNullException(  )
Solution to Question 16–5.

You can write multiple exception handlers to handle different exceptions; the first handler that catches the thrown exception will prevent further handling. Beware of inheritance complications in the ordering of your handlers.

Solution to Question 16–6.

If you have code that must run whether or not an exception is thrown (to close a file, for example), place that code in the finally block. You must have a try for the finally, but a catch is optional.

Solution to Exercise 16-1.

Create a Cat class with one int property: Age. Write a program that creates a List of Cat objects in a try block. Create multiple catch statements to handle an ArgumentOutOfRangeException, and an unknown exception, and a finally block to simulate deallocating the Cat objects. Write test code to throw an exception that you will catch and handle.

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{

   class Cat
   {
      private int age;

      public int Age
      {
         get { return age; }
         set { age = value; }
      }
      public Cat( int age )
      {
         this.age = age;
      }

   }



   class Tester
   {

      private void CatManager(Cat kitty)
      {
         Console.WriteLine ("Managing a cat who is " + kitty.Age +
                            " years old");
      }

      public void Run(  )
      {
         try
         {
            Console.WriteLine(
              "Allocate resource that must be deallocated here" );

            List<Cat> cats = new List<Cat>(  );
            cats.Add( new Cat( 5 ) );
            cats.Add( new Cat( 7 ) );

            CatManager( cats[1] ); // pass in the second cat
            CatManager( cats[2] ); // pass in the non-existent third cat

            Console.WriteLine(
            "This line may or may not print" );
         }

         catch ( System.ArgumentOutOfRangeException )
         {
            Console.WriteLine(
           "I've often seen a cat without a smile, \nbut this is " +
           "the first time I've seen a smile without a cat" );
         }

         catch (Exception e)
         {
            Console.WriteLine( "Unknown exception caught"  + e.Message);
         }

         finally
         {
            Console.WriteLine( "Deallocation of resource here." );
         }

      }


      static void Main(  )
      {
         Console.WriteLine( "Enter Main..." );
         Tester t = new Tester(  );
         t.Run(  );
         Console.WriteLine( "Exit Main..." );
      }
   }
}

The output from this example would look like this:

Enter Main...
Allocate resource that must be deallocated here
Managing a cat who is 7 years old
I've often seen a cat without a smile,
but this is the first time I've seen a smile without a cat
Deallocation of resource here.
Exit Main...

Your output may vary, depending on how you wrote your test code.

Solution to Exercise 16-2.

Modify Exercise 16-1 so that it does not throw an error. Create a custom error type CustomCatError that derives from System.ApplicationException, and create a handler for it. Add a method to CatManager that checks the cat’s age, and throws an error of type CustomCatError if the age is less than or equal to 0, with an appropriate message.

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{

    class Cat
    {
        private int age;

        public int Age
        {
            get { return age; }
            set { age = value; }
        }
        public Cat(int age)
        {
            this.age = age;
        }

    }

    // custom exception class
    public class CustomCatException :
    System.ApplicationException
    {
        public CustomCatException(string message)
            :
        base(message) // pass the message up to the base class
        {

        }
    }


    class Tester
    {

        private void CheckCat(Cat testCat)
        {
            if (testCat.Age <= 0)
            {
                // create a custom exception instance
                CustomCatException e =
                new CustomCatException("Your cat does not exist");
                e.HelpLink =
                "http://www.libertyassociates.com/NoZeroDivisor.htm";
                throw e;
            }
        }


        private void CatManager(Cat kitty)
        {
            CheckCat(kitty);
            Console.WriteLine("Managing a cat who is " + kitty.Age +
                              " years old");
        }

        public void Run(  )
        {
            try
            {
                Console.WriteLine("Allocate resource that must be deallocated
                                   here");

                List<Cat> cats = new List<Cat>(  );
                cats.Add(new Cat(7));
                cats.Add(new Cat(-2));

                CatManager(cats[0]); // pass in the first cat
                CatManager(cats[1]); // pass in the second cat

                Console.WriteLine(
                "This line may or may not print");
            }

            // catch custom exception
            catch (CustomCatException e)
            {
                Console.WriteLine(
                "\nCustomCatException! Msg: {0}",
                e.Message);
                Console.WriteLine(
                "\nHelpLink: {0}\n", e.HelpLink);
            }

            catch (System.ArgumentOutOfRangeException)
            {
                Console.WriteLine(
               "I've often seen a cat without a smile, \nbut this is " +
               "the first time I've seen a smile without a cat");
            }

            catch (Exception e)
            {
                Console.WriteLine("Unknown exception caught" + e.Message);
            }

            finally
            {
                Console.WriteLine("Deallocation of resource here.");
            }

        }

        static void Main(  )
        {
            Console.WriteLine("Enter Main...");
            Tester t = new Tester(  );
            t.Run(  );
            Console.WriteLine("Exit Main...");
        }
    }
}

Chapter 17: Delegates and Events

Quiz

Solution to Question 17–1.

The following is the standard way to define a delegate:

public delegate void PhoneRangHandler
( object sender, EventArgs e );

public event PhoneRangHandler PhoneRang;
Solution to Question 17–2.

Reference types.

Solution to Question 17–3.

To decouple the method(s) called from the calling code. It allows the designer of an object to define the delegate, and the user of the object to define which method will be called when the delegate is invoked.

Solution to Question 17–4.

You instantiate a delegate like this:

OnPhoneRings myDelegate = new OnPhoneRings(myMethod);
Solution to Question 17–5.

Here is how to call a delegated method:

OnPhoneRings(object this, new EventArgs(  ));
Solution to Question 17–6.

The ability for more than one method to be called through a single delegate.

Solution to Question 17–7.

Limits the use of the delegate in the following ways:

Solution to Question 17–8.

Define the delegate to take, as its second parameter, an object of a type derived from EventArgs. Pass the information through properties of that object.

Solution to Question 17–9.

None.

Solution to Question 17–10.

Rather than creating a method that matches the delegate’s signature and then assigning the name of that method to the delegate, you can directly assign an unnamed delegate method by providing the implementation in line with the assignment.

Solution to Exercise 17-1.

Write a countdown alarm program that uses delegates to notify anyone who is interested that the designated amount of time has passed:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace Exercise1
{
   // a class to hold the message to display
   public class CountDownClockEventArgs : EventArgs
   {
      public readonly string message;
      public CountDownClockEventArgs( string message )
      {
         this.message = message;
      }
   }

   // The subject (publisher). The class to which other
   // classes will subscribe. Provides the delegate TimeExpired
   // that fires when the requested amount of time has passed
   public class CountDownClock
   {
      private DateTime startingTime;
      private DateTime targetTime;
      private string message;

      // tell me the message to display, and how much time
      //(hours, minutes seconds) to wait
      public CountDownClock( string message, int hours, int mins, int seconds )
      {
         this.message = message;
         startingTime = DateTime.Now;
         TimeSpan duration = new TimeSpan( hours, mins, seconds );
         targetTime = startingTime + duration;
      }

      // the delegate
      public delegate void TimesUpEventHandler
      (
           object countDownClock,
           CountDownClockEventArgs alarmInformation
      );

      // an instance of the delegate
      public TimesUpEventHandler TimeExpired;

      // Check 10 times a second to see if the time has elapsed
      // if so, if anyone is listening, send then message
      public void Run(  )
      {
         for ( ; ; )
         {
            // sleep 1/10 of a second
            Thread.Sleep( 100 );  // milliseconds

            // get the current time
            System.DateTime rightNow = System.DateTime.Now;

            if ( rightNow >= this.targetTime )
            {
               if ( TimeExpired != null )
               {
                  // Create the CountDownClockEventArgs to hold the message
                  CountDownClockEventArgs e =
                    new CountDownClockEventArgs( this.message );
                  // fire the event
                  TimeExpired( this, e );
                  // stop the timer
                  break;
               }     // end if registered delegates
            }        // end if time has passed
         }           // end forever loop
      }              // end run
   }                 // end class

   // an observer.
   public class CountDownTimerDisplay
   {
      CountDownClock.TimesUpEventHandler myHandler;

      public CountDownTimerDisplay(CountDownClock cdc)
      {
         myHandler = new CountDownClock.TimesUpEventHandler( TimeExpired );

         // register the event handler and start the timer
         cdc.TimeExpired += myHandler;
      }

      // Alert the user that the time has expired
      public void TimeExpired( object theClock, CountDownClockEventArgs e )
      {
         Console.WriteLine( e.message );
      }
   }

   // an observer.
   public class CountDownTimerLog
   {
      CountDownClock.TimesUpEventHandler myHandler;

      public CountDownTimerLog( CountDownClock cdc )
      {
         myHandler = new CountDownClock.TimesUpEventHandler( TimeExpired );

         // register the event handler and start the timer
         cdc.TimeExpired += myHandler;
      }

      // Alert the user that the time has expired
      public void TimeExpired( object theClock, CountDownClockEventArgs e )
      {
         Console.WriteLine( "logging " + e.message );
      }
   }

   public class Test
   {
      public static void Main(  )
      {
         Console.Write( "Message: " );
         string message = Console.ReadLine(  );

         // NB: You would of course create a more sophisticated interface to
         // let the user set the correct amount of time. For now, we'll just
         // ask for how many seconds to wait
         Console.Write( "How many seconds?: " );
         int seconds = Convert.ToInt32( Console.ReadLine(  ) );

         CountDownClock cdc = new CountDownClock( message, 0, 0, seconds );
         CountDownTimerDisplay display = new CountDownTimerDisplay( cdc );
         CountDownTimerLog logger = new CountDownTimerLog (cdc);
         cdc.Run(  );

      }
   }
}
Solution to Exercise 17-2.

Break the program you write in Exercise 17-1 by assigning a new handler to the delegate (deleting the old!).

The only change is in the registration of the handler, using = rather than +=:

public class CountDownTimerLog
{
   CountDownClock.TimesUpEventHandler myHandler;

   public CountDownTimerLog( CountDownClock cdc )
   {
      myHandler = new CountDownClock.TimesUpEventHandler( TimeExpired );

      // register the event handler and start the timercdc.TimeExpired = myHandler;
   }

   // Alert the user that the time has expired
   public void TimeExpired( object theClock, CountDownClockEventArgs e )
   {
      Console.WriteLine( "logging " + e.message );
   }
}
Solution to Exercise 17-3.

Fix the program you wrote in Exercise 17-1 by using the event keyword and test against changes you added in Exercise 17-2.

The only change is in the declaration of the instance of the delegate, adding the keyword event, which will make Exercise 17-2 fail at compile time. Repairing it from = to += will let it run as an event.

// the delegate
public delegate void TimesUpEventHandler
(
     object countDownClock,
     CountDownClockEventArgs alarmInformation
);

// an instance of the delegate
publicevent TimesUpEventHandler TimeExpired;

Chapter 18: Creating Windows Applications

Quiz

Solution to Question 18–1.

Click on the control on the form, then set the properties in the Properties window.

Solution to Question 18–2.

Implement an event handler.

Solution to Question 18–3.

The two ways to create an event handler are as follows:

  • Go to the Properties window, click on the lightning button to open the events, then fill in a name or double-click next to the event to let Visual Studio 2005 create the name.

  • Double-click on the control to create the default handler with a name provided by Visual Studio 2005.

Solution to Question 18–4.

A method calling itself (such as calling MethodA( ) from within the body of MethodA( )).

Solution to Question 18–5.

XML documentation comments are preceded with three slashes, and are used to generate XML documents that can be used to document the application.

Solution to Question 18–6.

Click the ShowAllFiles button on the Solution Explorer and examine the <FormName>.Designer file.

Exercises

Solution to Exercise 18-1.

Create a Windows application that displays the word “Hello” in a label, and has a button that changes the display to “Goodbye.”

See the source code solution Chapter18Exercise1 on the web site for this book. Figure A-5 is a picture of the form.

Here is the event handler for the button:

private void button1_Click( object sender, EventArgs e )
{
   label1.Text = "Goodbye";
}
Solution to Exercise 18-2.

Create a Windows application that presents an order form that looks like Figure A-6.

This figure represents an order form that that lets the user enter information through various controls such as buttons, checkboxes, radio buttons, DateTimePicker, and so forth. You don’t need to write the back-end for the ordering system, but you can do the following:

For one example solution, please see the source code solution Chapter18Exercise2 available on the web site for this book.

To make this work the way I wanted, I added a bit of code to support the UI (though not to support actually recording the data). That code follows:

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

namespace Chapter18Exercise2
{
   public partial class OrderForm : Form
   {
      public OrderForm(  )
      {
         InitializeComponent(  );
         ResetValues(  );
         ResetDeliveryDate(  );
      }

      // When you click the Add button we reset for next choice
      // and we do the work of adding the current order to the cart
      private void btnAdd_Click( object sender, EventArgs e )
      {
         ResetValues(  );
         SaveToShoppingCart(  );
      }

      // set all the controls to their starting values
      private void ResetValues(  )
      {
         this.ddlBooks.SelectedIndex = -1;
         this.ddlBooks.Text = "Please pick a book";
         this.numCopies.Value = 1;
         this.rbHardcover.Checked = false;
         this.rbLibrary.Checked = false;
         this.rbPaperback.Checked = true;
         this.cbConfirm.Checked = false;
         this.cbGiftWrap.Checked = false;
         this.cbRush.Checked = false;
      }

      // broken out in case we need to set this separately
      private void ResetDeliveryDate(  )
      {
         // minimum delivery date is 2 days from now
         this.deliveryDate.MinDate = DateTime.Now + new TimeSpan( 2, 0, 0, 0 );
         // initialize the control to the MinDate
         this.deliveryDate.Value = this.deliveryDate.MinDate;
      }

      // handle work to save to shopping cart and update status
      private void SaveToShoppingCart(  )
      {
         // work here to record choices
         this.lblStatus.Text = "Saved to Shopping Cart.";
      }

      // hack to allow touching any control other than button
      // to turn off the status (to avoid confusion)
      private void UniversalEventHandler( object sender, EventArgs e )
      {
         this.lblStatus.Text = string.Empty;
      }
   }     // end class
}        // end namespace
Solution to Exercise 18-3.

Modify the first exercise by dragging a timer (found in the Components section of the Toolbox) onto the form and having the timer change the message from “Hello” to “Goodbye” and back once per second. Change the button to turn this behavior on and off. Use the Microsoft Help files to figure out how to use the timer to accomplish this exercise.

  1. Create a new project named Chapter18Exercise3.

  2. Set the form to the size of the form in Exercise 18-1 ( 196,128).

  3. Optionally copy the two controls (label and button) from the first exercise (or drag on new ones).

  4. Set the form’s text to “Hello Goodbye.”

  5. Drag a timer onto the form; it will appear in the tray, as shown in Figure A-7.

  6. Set the timer’s Interval property to 1000 and Enabled to false.

  7. Double-click on the timer to create the timer1_Tick event handler that will fire every 1,000 milliseconds (every 1 second). It will alternate the text in the label by checking (and changing) the Boolean value isHello.

  8. Change the button event handler to test if the timer is running (if so, its IsEnabled property is true) to start the timer and set the button’s text to stop, or to stop the timer and set the button’s text to start.

The source code for this example is presented in Example A-1.

Chapter 19: Programming ASP.NET Applications

Quiz

Solution to Question 19–1.

System.Web and System.Web.UI.

Solution to Question 19–2.

On the server (though occasionally some Web Controls will write script to run on the client).

Solution to Question 19–3.

Making a non-postback event into a postback event is accomplished simply by setting the AutoPostBack property to true.

Solution to Question 19–4.

View state is the ability of ASP.NET pages to store the state of their controls during a post-back; the net effect is that the programmer does not have to write code to maintain the state of controls when a page is posted back to the server.

Solution to Question 19–5.

You must update the Page directive to ensure that the Inherits property is set to the new class name.

Solution to Question 19–6.

Click “Add a new web.config file with debugging enabled,” and Visual Studio 2005 will create a web.config file for you, or alternatively, you can create the file yourself (by hand) and then add it to the project.

Solution to Question 19–7.

The three ways are as follows:

  • Drag them from the toolbox.

  • Write the code in the HTML markup window.

  • Add them programmatically.

Solution to Question 19–8.

Every Server Control must have this property:

runat="Server"
Solution to Question 19–9.

The DataSource control.

Solution to Question 19–10.

A series of <input> elements of type “radio” with associated <label> controls, all contained within a <table>.

Solution to Question 19–11.

Test for the IsPostBack property of the page. If true, the page has been posted back.