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.
Console, Windows, Web, and Web Services.
The Framework specifies how .NET constructs intrinsic types, classes, interfaces, etc.
The Framework Class Library provides a very large suite of predefined classes that you can use in building your application.
This refers to “type safety”—the ability of the compiler to ensure that the objects you create are of the expected type.
Keywords are reserved for use by the language and cannot be used to identify objects or methods you create.
Namespaces are used to ensure that identifiers are unique across libraries of classes.
The job of the compiler is to turn your source code into MSIL.
The Microsoft Intermediate Language is the native language for .NET and is compiled into an executable application by the JIT.
The Just In Time compiler turns your MSIL code into an application in memory.
The instructions for creating this program are found in Chapter 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.
Click and drag on the title bar; use the indicators for placement.
Toggles between lock in place, and hide as a tab.
F5 indicates run with debugging; Ctrl-F5 indicates run without debugging.
Allows you to store more than one item on the clipboard.
Ctrl-Shift-V cycles through all the selections.
Allows you to search for symbols (namespaces, classes, and interfaces) and their members (properties, methods, events, and variables).
A tool for returning to a specific place in your code.
An editing tool to help you find the right method and/or the correct parameters and much more.
A complete outline of a commonly used programming structure with replaceable items to speed development.
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.
Undock the Solution Explorer window from the right side of the IDE and move it to the left. Leave it there if you like or move it back.
Your Visual Studio window should look something like Figure A-2.
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.
True or False.
int
is the C# alias for
the .NET Int32
type. They can
be used interchangeably.
All except the last, which requires an explicit cast:
int newInt = (int) myLong;
A uint
is an unsigned
int
and can hold only
positive numbers, but it can hold a positive value twice as big
as an int
(4 million+ rather
than 2 million+).
A float
takes four
bytes and a double
takes
eight bytes, and thus a double
can represent much larger
values with greater precision.
In C, if you wish to use a variable of any type (pass it as a parameter to a method), you must assign it a value.
You refer to the constant like this:
Temperatures.LightJacketWeather
Its value is 33.
Any statement that evaluates to a value.
Write a short program creating and initializing each of
the following types of variables: int
, float
, double
, char
, and then outputting the values
to the console.
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.
Modify the program in Exercise 3-1 to change the values of variables and output the values to the console a second time:
namespace fundamentals { class exercise { static void Main( ) { // round one int myInt = 3; float myFloat = 4.25f; double myDouble = 123456789.9867; char myChar = 'A'; System.Console.WriteLine("Round 1: myInt: {0}, myFloat: {1}, myDouble: {2}, myChar: {3}.", myInt, myFloat, myDouble, myChar); // round two myInt = 5; float myFloat = 25.267f; myDouble = 987654321.1234; myChar = 'Z'; System.Console.WriteLine("Round 2: myInt: {0}, myFloat: {1}, myDouble: {2}, myChar: {3}.", myInt, myFloat, myDouble, myChar); } } }
The output should look like this:
Round 1: myInt: 3, myFloat: 4.25, myDouble: 123456789.9876, myChar: A. Round 2: myInt: 5, myFloat: 25.267, myDouble: 987654321.1234, myChar: Z.
Modify the program in Exercise 3-2 to declare a constant
float Pi
equal to 3.14159.
Then assign a new value to pi (3.1) and output its value with
the other variables statement. What happens when you try to
compile this program?
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.”
The fourth expression evaluates to 5, not to true
. Thus, if you write:
z = x = y;
and y
has the value 5, then
the order of operations is that the value in y
(5) is assigned to x
, and the value of the expression x=y
, which is 5, is assigned to z
.
Write a program that assigns the value 25 to variable
x
, and 5 to variable y
. Output the sum, difference,
product, quotient, and modulus of x
and y
.
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.
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
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
if
, else
, switch
.
False. In C#, an if
statement’s condition must evaluate to a
Boolean expression.
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.
Either a numeric value or a string.
False. If the statement has no body, then you can fall through. For example:
case morning: case afternoon: someAction( ); break;
Two uses of goto
are:
To go to a label in your code
To go to a different case
statement in a switch
statement
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.
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.
Two ways of creating an infinite loop are:
for (;;) while(true)
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" ); } }
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; } } } }
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
New (user-defined) types are most often created in C# with
the keyword class
.
A class defines a new type; an object is an instance of that type.
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.
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.
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.
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.
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).
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.
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.
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.
Draw a class diagram for a class named “vehicle” and show how the classes “car,” “truck,” and “motorcycle” derive from it. (See Figure A-4.)
Define a class Book
, in
which a book has a title, author, and ISBN, and the book can be
read or shelved:
class Book { private String title; private String author; private String ISBN; public Book (string title, string author, string ISBN) { this.title = title; this.author = author; this.ISBN = ISBN; } public void Read( ) // member method { // code here to read book } public void Shelve( ) // member method { // code here to shelve book };
A class defines a new type; an object is a single instance of that type.
Instances of classes are reference types and are created on the heap.
Intrinsic types (such as integers) and structs are value types and are created on the stack.
Access is limited to methods of the defining class.
Access is available to methods in any class.
The class’s constructor is called.
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.
None. A constructor is not defined to return a type, and is not marked void.
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.
this
refers to the
object itself—the current instance of the class.
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( )
.
The using
statement
automatically calls the dispose method on the object once the
statement completes.
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); } } }
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); } } }
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.
The signature of a method is its name and its parameter list.
Properties are public accessors to your encapsulated data. Properties appear to the class creator as methods, but to the class’s clients as fields.
Do not implement the set
part of the property. No special
notation is required.
By passing in parameters by reference and getting the results back in those parameters.
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.
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.
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( ); } } }
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( ); } } }
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( ); } } }
Go to the line where you want execution to stop, and click in the margin. A red dot will appear on the line.
Pressing F10 steps over a method; F11 steps into the method.
Right-click on the line and choose breakpoints and then disable breakpoint, or right-click on the breakpoint and choose disable.
The Locals window shows all the variables that are in scope. The Autos window shows variables used in the current and previous statement.
Either right-click on the variable and choose “Add to Watch window” or just click and drag the variable directly onto the Watch window.
Right-click on the variable and choose “QuickWatch,” or select Debug → QuickWatch.
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.
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( ); } } }
Place a breakpoint in Run( )
on the following line:
System.Console.WriteLine("Before staring: \n value of myInt: {0} \n value of myFloat: {1}", myInt, myFloat);
Step into the Multiply( )
method, up to the call to Divide( )
. What are the values of
theInt
and theFloat
at this point?
Run the program again, and when it reaches the
breakpoint in Run( )
, set
a watch on myInt
. Step
through the methods. When does the value of myInt
change?
Set another breakpoint in Add( )
at this line:
theInt = theInt + theInt;
Run the program. How many calls are in the call stack when the program reaches this breakpoint?
Answers:
As shown in the Locals window, myInt
is 42 and myFloat
is 9.685, because both
have just been set.
Use theInt
is 84
and theFloat
is
19.37.
The value of myInt
doesn’t change until control returns to Run( )
, after the Multiply( )
method has
finished.
There are five calls in the call stack at this point:
Main( )
, Run( )
, Multiply( )
, Divide( )
, and Add( )
.
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.
6. Arrays always begin with index (or offset) zero.
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.
Arrays are reference types and created on the heap.
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.
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 };
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.
A rectangular array is a multidimensional array; a jagged array is an array of arrays.
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( ); } } }
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( ); } } }
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( ); } } }
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( ); } } }
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.
Through inheritance.
class <identifier> : <base class>
Create a virtual method in the base class, and then override it in the derived class.
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.
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.
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.
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.
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.
Object
(which is the
ultimate base class (root) of all types in C#).
Object
.
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.
When you return a boxed type back to its original type, you must unbox it and explicitly cast it.
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( ); } }
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( ); } }
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( ); } }
The process of writing methods for your class that allow
clients of your class to interact with your class using standard
operators (+
, ==
).
Static methods.
Call to:
public static Fraction operator+(f2, f1)
Overload the Equals( )
method.
Use implicit conversion when you know the conversion will succeed without the risk of losing information. Use explicit conversion if information might be lost.
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( ); } }
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( ); } }
The interface defines the methods, properties, etc. that the implementing class must provide. The implementing class provides these members and, optionally, additional members.
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.
class MyClass : MyBase, ISuppose, IDo {}
Note that the base class must come first after the colon.
The is
and the as
operator.
is
returns false if the
interface is not implemented; as
returns null. Using the as
operator can be more
efficient.
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.
ExtendedInterface
:
OriginalInterface
For example, read:
ILoggedCompressible : ICompressible
as “ILoggb[edCompressible extends ICompressible.”
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.
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.
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 ); } }
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
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
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
Indexers are unnamed. You use the this
keyword to create an
indexer:
public string this[int index]
Any type can be used, although it’s most common to use integers.
The elements of the array must implement IComparable
.
Generics allow you to create type-safe collections without specifying the type the collection will hold when you create the collection.
It allows your collection to support a foreach
loop.
The size of an array is fixed when you create it. A
List<T>
expands
dynamically when you add more elements.
A List is like an expandable array. A Stack is a “Last In, First Out” collection, a Queue is a “First In, First Out” collection, and a Dictionary is a collection of key/value pairs where you can retrieve the value given a key.
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 } } } }
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 ); } } }
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 ); } } }
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
string
(lowercase) is
the C# keyword that maps to the .NET Framework String
class. They may be used
interchangeably.
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
A quoted string, provided by the programmer, such as “Hello.”
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.
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.
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.
It is not possible to derive from the String
class (or any other sealed
class).
You can call the Concat
method of the String
class,
but it is more common to use the overloaded +
operator.
Given an array of delimiters, Split( )
returns the substrings of the
original string.
StringBuilder
objects
are mutable. When the StringBuilder
has the complete set of
characters you want, you call ToString( )
to get back a string object.
Regular expressions constitute a language for identifying and manipulating strings using both literal and metacharacters.
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:
Output the length of each string.
Output the third character in each string.
Output whether the character “H” appears in each string.
Output which strings are the same as string 2.
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( ); } } }
Take the following string:
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.
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( ); } } }
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.
The stack is unwound until a handler is found, or else the exception is handled by the CLR, which terminates the program.
You create a try
/catch
block; the catch
part is the exception
handler.
The syntax is:
throw new Sytem.Arg'umentNullException( )
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.
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.
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.
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..."); } } }
The following is the standard way to define a delegate:
public delegate void PhoneRangHandler ( object sender, EventArgs e ); public event PhoneRangHandler PhoneRang;
Reference types.
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.
You instantiate a delegate like this:
OnPhoneRings myDelegate = new OnPhoneRings(myMethod);
Here is how to call a delegated method:
OnPhoneRings(object this, new EventArgs( ));
The ability for more than one method to be called through a single delegate.
Limits the use of the delegate in the following ways:
You can only add a method using +=
.
You can only remove a method using -=
.
The delegate can only be invoked by the class that defines it.
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.
None.
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.
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( ); } } }
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 );
}
}
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;
Click on the control on the form, then set the properties in the Properties window.
Implement an event handler.
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.
A method calling itself (such as calling MethodA( )
from within the body of
MethodA( )
).
XML documentation comments are preceded with three slashes, and are used to generate XML documents that can be used to document the application.
Click the ShowAllFiles button on the Solution Explorer and
examine the <FormName>.Designer
file.
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"; }
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:
Simulate saving to the shopping cart with a message, and reset the form when the “Add to Shopping Cart” button is clicked.
Set the minimum delivery date to be two days from now, and let the user select later dates.
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
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.
Create a new project named Chapter18Exercise3.
Set the form to the size of the form in Exercise 18-1 ( 196,128).
Optionally copy the two controls (label and button) from the first exercise (or drag on new ones).
Set the form’s text to “Hello Goodbye.”
Drag a timer onto the form; it will appear in the tray, as shown in Figure A-7.
Set the timer’s Interval property to 1000 and Enabled to false.
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.
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.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace Chapter18Exercise3 { public partial class Form1 : Form { bool isHello = true; public Form1( ) { InitializeComponent( ); } private void button1_Click( object sender, EventArgs e ) { if ( !timer1.Enabled ) { timer1.Start( ); this.button1.Text = "Stop"; } else { timer1.Stop( ); this.button1.Text = "Start"; } } private void timer1_Tick( object sender, EventArgs e ) { isHello = !isHello; if ( isHello ) label1.Text = "Hello"; else label1.Text = "Goodbye"; } } }
System.Web
and System.Web.UI
.
On the server (though occasionally some Web Controls will write script to run on the client).
Making a non-postback event into a postback event is
accomplished simply by setting the AutoPostBack
property to true.
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.
You must update the Page directive to ensure that the Inherits property is set to the new class name.
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.
The three ways are as follows:
Drag them from the toolbox.
Write the code in the HTML markup window.
Add them programmatically.
Every Server Control must have this property:
runat="Server"
The DataSource
control.
A series of <input>
elements of type “radio”
with associated <label>
controls, all contained within a <table>
.
Test for the IsPostBack
property of the page. If true, the page has been posted
back.
For the answers to these exercises, please download the ASP.NET project files that are available on this book’s web site.