0% found this document useful (0 votes)
933 views1,392 pages

Programming in Visual Studio 2017 C# - Combined PDF

Uploaded by

Sergeyups Sergey
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
933 views1,392 pages

Programming in Visual Studio 2017 C# - Combined PDF

Uploaded by

Sergeyups Sergey
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 1392

An Absolute Beginners Guide to C# - Volume 1

Visual Studio C# 2017


Intro -Through Forms
by Tim R. Wolf
2017.06 Version 1.04
Introduction

Audience:

This book is volume one of a three volume set and is intended for beginning Microsoft
Visual Studio C# (C Sharp) programmers, versions 2015 through VS 2017. This is a
hands-on book, with actual programs, starting in Chapter 1 and it talks about the
practical day-to-day, nuts-and-bolts programming that real people need to know.

It assumes no prior programming experience.


Little time is spent on theory.

Each topic has step-by-step instructions with numerous code examples and over 900
cropped and annotated illustrations. It explains the "why" of a program.

Prior Experience

If you already have moderate programming experience, especially in Visual Basic, this
book will expand your skills, giving confidence in the new language.

The goal is to have as much time on the keyboard, working with common business
problems. After studying this book and working through the examples, you will be a
proficient programmer – able to write real programs that do real work. You will be able
to read files, parse data, write to database, and build data input screens.

Why this book?

This book is different than most publications. Little time is spent on theories and
technical side-trips are rare. Starting in Chapter 1, you will immediately begin working
with loops, if-statements and string-manipulation. This means some topics, such as
conversions, numeric types, and other such concepts, are glossed over until they are more
germane to the concepts being taught.

Where other publications might spend a page or two on a topic, this book dives into the
most common and most useful ways to solve a problem. For example, over 80 pages are
devoted to opening multiple forms and how to pass data between them. The parsing
chapter devotes 70 pages to this subject, covering delimiters, CSV, Tab, Excel, and other
techniques. This is not over-kill. You will find these address real-world data-processing
problems. I cover the tips and tricks you will need to know.

Chapters often show different techniques for the same problem and the benefits and
drawbacks of each are explained. If there is a chance of making a mistake in
punctuation, style, or logic, the examples show how to resolve them. Compiler errors are
scattered throughout the book and there is a comprehensive alphabetic error reference in
the appendix, showing likely causes and recommendations.

A side-effect of this first volume will be a library of utility modules that can be used in
all of your programs. These utilities can automate mundane tasks, such as parsing
delimited files, punctuating phone numbers, street-addresses, and capitalizing proper
names. These libraries will save boat-loads of time and will be literally useful in all your
programs.

You will also notice none of the examples use Console-applications (DOS-like
programs) – all are Windows forms. Besides being more visually interesting, they allow
greater feedback while developing and the programs more accurately reflect what
happens in the business world.

What this book does not cover:

I do not cover some of the more theoretical underpinnings of modern programming.


Things such as polymorphism and object-inheritance are not discussed directly, but the
techniques are used throughout the book. Advanced programmers may take exception,
but I favor a more procedural background and I always approach problems from a
business-oriented perspective. You have a job to do, files to process, data-entry-screens
to write. These are the things this book is concerned about.

These volumes do not cover web-development but the programming skills taught are
100% transferrable. SQL databases are introduced, but four lengthy chapters only
scratch the surface. However, if you fear treading in this area, these four chapters will
get you started and will show relatively advanced techniques.

Why C#?:

Microsoft has built a wonderful programming environment which verges on magical.


The language is mature and consistent and can be applied in any conceivable situation.
It is a powerful tool that can solve complex problems.

The language is capable and mature. Even if this is your first programming language,
you will be pleased at its versatility and ease.

How to Use This Book:

These three volumes are teaching books and because of that, it makes a poor reference
guide. To get the most utility, start at page one and work your way through chapters.
Each chapter builds on the previous. It takes time and effort to learn programming. As
you work through the chapters you must sit in front of the compiler and write the code.

This series was divided into three volumes, partly to aid in printing, and partly to make
the chapters more accessible.

Volume 1:
1 Introduction to the Editor
2 Introduction to Loops
3 Conditional Branching
4 Strings
5 Numbers and Dates
6 Utility Functions
7 Advanced Utility Functions
8 Class Libraries
9 Variable Scope
10 Form Controls and Events
11 Calling Multiple Forms
A Compiler Error Messages
B Compile and Distribute

Volume 2:
12 ASCII Files
13 Parsing Tab and CSV Files
14 INI Files
15 XML and App.config Files
16 Windows Registry
17 Reading Excel and Access
18 External Programs (Shell)
19 Wait, Delays, Pauses
20 Printing
21 Formatting

Volume 3:
22 Arrays
23 File Manipulation
24 Console Applications
25 SQL Databases
26 SQL Record Edits
27 SQL Data Grids
28 Data Grid Cell Editing
C Installing SQL Server Express
D Routines (of Interest)

Thank you

Thank you for purchasing this book. I hope you enjoy it as much as I have had writing it.
Comments and suggestions are welcome.

traywolf at keyliner com


www.keyliner.com

This book was written with Visual Studio 2013 through 2017's Community Edition, with
sections referencing Microsoft Office and Microsoft SQL Server 2016 Express.
© 2017 by Tim R.Wolf. All rights reserved.

Original text written with WordPerfect version X7. Illustrations created with Corel's
PaintShop Pro, version X8.
The Compiler and Other Tools:

As of mid 2017, Microsoft allows you to freely download a fully-capable version of


Visual Studio. The software is free and this book was written with these versions. See
this link:

http://www.visualstudio.com/en-us/products/visual-studio-community-vs

This book is applicable to VS 2010 and newer, with an emphasis in version 2017.

For the database chapters you will need a copy of Microsoft MS-SQL database or a copy
of Microsoft's free downloadable "MS-SQL Express". Details can be found in the
Appendixes.

Everything in this book was designed to run on a stand-alone (home) workstation,


including the SQL chapters. You will not need a server, external SQL database or
Active Directory to complete the chapters. However, when useful, references to these
other resources are made.
Conventions

Printing conventions are designed to help you find pertinent code quickly.

Code examples are show in plain text, white text boxes or shaded text boxes. The
boxing and shading gives you a clue about how "solid" or complete the code is. If you
are skimming through the chapters looking for something, the formatting style tells you
how far along the text is.

The formatting styles move from discussions to preliminary to final code.

At the beginning of each chapter is a summary or outline of the most common commands
or coding techniques. These act as a reference for the details that follow.

Discussion Code Blocks:

Code that is in-line with the text, and without a text-box border, is a discussion or
theoretical block and you are not expected to type or test. For example, this is a
discussion code block:

//Consider showing which number was selected


MessageBox.Show("Selected value: " + Convert.ToString(inumericValue));

Code is always typed in a fixed-width, non-proportional Courier font.


Author Comments:

If I have an opinion on a programming technique, it is typed as an indented, italicized


block, such as this:

In real life, no programmer would define a "part-number" as a


floating-point number, even if it has a decimal point. This
should really be declared as a string. Only declare numeric
values if you intend to perform mathematical operations.

Paragraph Numbering:

There may be numerous steps to solve a programming goal and you will find I am a fan
of numbering them. Within each section, you will find numbered steps, 1, 2, 3 or
A, B, C.

Setup or pre-requisite steps, as well as testing, are numbered "A, B, C". Actual
programming steps (devoted to the section you are reading), are numbered "1, 2, 3".

Terminology:

Although I assume no previous programming knowledge, it is helpful to understand a


few terms.

"Variable"
A variable is a named place-holder for a value. For example, a field may be named
"FirstName" – the variable's name holds anybody's name, hence its variableness. Other
famous variable names include "i", which typically is nothing more than a counting
variable, where "i" might equal 10.

While programming, make-up or invent your own variable names. The names should be
descriptive, which helps identify its purpose later in the program and I favor long,
descriptive names: frontSideDelimiter and RecordCategoryCode.

"Strings"
Textual information (words, phrases, sentences, letters, and characters) are all called
"strings". Strings can be concatenated (appended to each other), or parsed (taken apart
into smaller, constituent pieces).

Examples of strings:
"Dog"
"M"
"A sentence, such as this one."
"Product ID: 3003-22"
"123 Elm Street"
"65,002"
"10/06/2007"
Strings, by their very definition are nothing but blobs of text. This is where most of your
computing time will be spent. Numbers and dates can be represented as strings, but
mathematical calculations can not be performed against them.

"literal"
A literal is an explicit text string, manually coded into the program. Some call this a
"hard-coded" value. Literals are enclosed in quotes. For example,

string FirstName = "John Smith";

where FirstName is a string variable (declared as a string) and "John Smith" is a literal,
typed in quotes. Literals can live inside of other statements, such as:

MessageBox.Show("Display this literal now");

"Numbers"
Numbers are more interesting. The general intention is to use them for calculations, such
as a summation, count, or other mathematical operations. There are amazingly a dozen
different numeric types, where the most common are "integers" (whole numbers, such as
1, 2, 3, 0, -5, 200, etc) and real numbers (such as 3.24, 7/8ths), which are also called
Floating Point numbers.

"boolean"
Booleans are yes-no, true-false variables and they are neither a string or a number.
Booleans are often expressed in the terms of an if-statement, such as:

if (FirstName == "Smith")

or sometimes booleans can be defined as a variable, as in:

bool EndofFileSwitch = false;

if (EndofFileSwitch)
if (EndofFileSwitch == true)

Note the double-equal signs.

"Declare"
The text will say "declare" (or define) a variable. This means you are creating a new
variable by telling the compiler what type of variable it is, and its name. Variables are
created by typing a statement, for example:

string FirstName = "John Smith";

where "string" declares a new variable "of type 'string'" with a declared name of
"FirstName". Variables do not have to be initialized with a value when they are
declared. For example, it can be declared on one line and populated on another:
string FirstName; //Defined or declared...

(then later...)
FirstName = "John Smith"; //Initialized or populated

Integer variables are declared in a similar fashion:

int loopCounter;
loopCounter = 15;

int totalCount = 120;

and with all variables, you can assign other similarly-typed values to another variable:

int loopCounter = 0;
int someOtherCounter = 200;
loopCounter = someOtherCounter; //where loopCounter now equals 200

After the first couple of chapters, I tend to prefix my variables with an indicator showing
what type of variable these are, for example, "str" for string, "i" for integer. This is a
documentation aid, and I use this style in my own programming:

strFirstName a string (text) variable for the person's first name.


strpassedPartNumber a string variable holding a part number

iCurrentRow the current row number being processed in a list


iCounter an integer (counting number, 1, 2, 3...) called "counter" because
a more descriptive name wasn't needed for a short-term routine.

cbxAddressType a checkbox field


lblSequenceNumber a "label" called "SequenceNumber" (on a form)
boolEndofFile a "boolean" (True/False) variable

Not all developers use prefixes and some are violently opposed to them.

"Visual Studio Project"


A Visual Studio "Project" is the program you are writing and it may consist of multiple
forms, libraries and code, all combined into one "solution file" (.sln). The technicalities
are minor. How to create, open and edit solutions and projects are described in the next
chapter.

Legal Stuff:
Visual Studio, Excel, Access, Visual Basic, Microsoft SQL, SQL Express, are Microsoft
Products with all of their copy-rights and trademarks

Let's get to work...


- TRW, 2017.01
Chapter 1 - Introduction to the Editor

This chapter acquaints you with Visual Studio's editing environment and basic syntax.
This is an introductory chapter and is hands-on; you will begin writing a program starting
on the next page.

Topics Covered:

• Your First Program


• Toolbox flyout
• +Common Controls Flyout sub-menu
• Dragging objects to the form
• Form Design tab and the Code Tab (Form1.cs)
• Labels
• Strings
• int (Integer)
• Convert.ToString
• Case Sensitivity
• Introduction to variable Scope
• Standard Text Boxes
• Assigning Strings to TextBox
• Properties Panel

Chapter 1 - The Editing Environment and First Program Page: 3


Your First Program

1. Begin by launching Microsoft Visual Studio or Microsoft Visual Studio Express, from
either the Start Menu or from an icon on your desktop.

2. Once the program opens, select File, New, Project from the top menu.
Or alternately, click "More project templates..." from the Start Page:

3. Visual C# displays a New Project dialog box.

• On the Installed Templates tree, left-side, expand "Visual C#" by clicking the
triangle (it may be already expanded).

• Within the tree, select the "Windows Classic Desktop", then choose "Windows
Forms App" (in older versions of Visual Studio, select "Windows"):

Chapter 1 - The Editing Environment and First Program Page: 4


• For now, accept the default/suggested filenames at the bottom of the screen (not
illustrated)

• Click OK.

4. The editor opens to a screen divided into several sections and some of the window panes
may not be displayed on first launch. Here are the main panes you need to know and
each are described in more detail later in this chapter:

• Solution Explorer – A tree-view of the project's components, including the main


form and any libraries you may use.

• Properties - Individual object properties, such as name, color, size, etc. In this same
pane are Events, such as "on click", "minimize", and others. The properties and
event panels are context sensitive.

• ToolBox - "Flyout" menus for design tools (for adding textboxes, buttons).

• Error List - A list of compiler and syntax errors while coding. This is likely hidden
and the steps to expose are described below.

Chapter 1 - The Editing Environment and First Program Page: 5


Exposing Window Panes:

When you open your first editing session, some panes, such as Error List, may not be
visible on the design screens. Open with these steps:

From the top menu, choose "View".


Expose the following, as needed:

View, "Solution Explorer" (probably already displayed)


View, "Properties Window"
View, "ToolBox" (click outside the box to have it dock back to the side)
View, "Error List" (see bottom of editing window, near the "Output" tab)

At the bottom of the editing screen is the Error List, but often, it is obscured by the
Output tab. Click the Output Tab's "X" to close that window.

Chapter 1 - The Editing Environment and First Program Page: 6


By default, the View panes "dock" at the sides of the editor, and it is recommended they
stay in these locations. By choice or accident, they can be ripped off the edge and can
float above the working surface. If a pane becomes un-docked, re-dock by dragging the
title-bar to a docking indicator, which appears as you start dragging. Good luck figuring
this out.

Chapter 1 - The Editing Environment and First Program Page: 7


The Toolbox flyout behaves differently. Clicking the left-margin tab, "Toolbox", opens
the flyout. Note the thumbtack, which can anchor the window open. Most developers
allow the toolbox to auto-hide when not needed:

Finally, near the top, just above the Form1 form, is a tab-bar, where "Form1.cs [Design]"
is highlighted. In a moment, the "Code View" will appear as a second tabbed item on
this top-row. Use these tabs to switch between the form's design and code editing views.

First Program:

Begin the first program by adding a "text box" (data-entry field) and a button to the
default form, Form1. The things you are adding are called "objects."

1. In design view, in the center of the screen, confirm "Form1" is visible.

Chapter 1 - The Editing Environment and First Program Page: 8


• Click anywhere inside the form to activate the object, noting the black "handles"
(knobs) along the perimeter, indicating the form is active and re-sizeable.

• Click the Toolbox tab, found on the left-edge of the screen. The menu will fly-out.
(If the Toolbox flyout is not shown, select View, "Toolbox" from the menu-bar at the
top of the screen.)

• From the Toolbox flyout, scroll to "Common Controls"; open the list by clicking the
triangle (older versions of Visual Studio use "+").

Chapter 1 - The Editing Environment and First Program Page: 9


2. Click-and-drag the "Button" menu-item from the menu to the form, as illustrated.
Drop the button anywhere on the form; it doesn't matter where. The flyout may obscure
part or most of the form; drag past the form and the flyout will slink out of the way. The
button is automatically named "button1".

3. Drag a second Button, "button 2" to your form. This will be used in a later example.

4. From the same flyout-menu, drag a "Label" object to the form.


A "label" is simply visible text on your screen and it can say anything. For now, leave it
at it's default value, "label1". Later, this program will programmatically change its
value.

Click and drag the new objects, maneuvering them until they appear similar to the
illustration below:

Chapter 1 - The Editing Environment and First Program Page: 10


5. Still in design view, double-click button1.

This opens the screen into Code View - e.g. programming view and stubs-in a new
method called "button1_Click". Notice the new tab on the top row: "Form1.cs", where
".cs" stands for "C-Sharp". Alternately, in Solution Explorer, right-mouse-click
"Form1.cs" and choose "View Code / F7" and type the following code by hand.

Chapter 1 - The Editing Environment and First Program Page: 11


6. In the lower pane of the editor, notice the Error List. You may have this message:

"IDE1006 Naming rule violation". This is new to Visual Studio 2017 and indicates a
method or procedure should begin with a capital letter (e.g. "Button1_Click"). For now,
this can be ignored.

(If the Error List is not visible on the bottom-left of the screen, select top-menu View,
"Error List". Or, the tab may be obscured by the "Output" tab.)

Additional notes: This book was written using Visual Studio 2017 Release Candidate 1
and may change once the program is released.

7. In the displayed code, locate the line

"private void button1_Click (object sender, EventArgs e)"

Type the following statements between the opening and closing {braces}, starting with
the line "string myFirstWordA;" Each word is upper/lowercase sensitive and each
statement ends in a semi-colon ( ; ). Indent with spaces or tabs, but you can be sloppy
and the editor will align and indent after pressing Enter.

Program 1.1, completed


private void button1_Click(object sender, EventArgs e)
{
string myFirstWordA;

myFirstWordA = "Cat";
label1.Text = myFirstWordA;
}

Important Notes:

In the code, pay close attention to the opening and closing {braces}. The new code must
live between the braces directly below the "private void button1_Click" statement.
Notice the two closing braces at the bottom of the program – these belong to the
solution's namespace and are not part of the button1 block.

Chapter 1 - The Editing Environment and First Program Page: 12


The number-one problem beginning programmers have is mis-placing the braces or
typing outside of the correct bracing. The editor can help you find a paired-brace; click
the mouse directly behind the closing brace; notice how the top (matching) brace
highlights in a subtle gray.

Everything in C# is case-sensitive (upper and lower-cased letters are important).


Keywords, commands and invented variables, such as "myFirstWordA", are case-
sensitive. It is common to have mixed-cases, as in, "label1.Text" and "button1_Click".

Almost every statement, but not all, ends with a semi-colon, " ; ". These mark the end of
the statement. Longer, more complicated statements may take multiple lines in the
editing screen, but it is the closing semi-colon that marks the end of the command. As
you progress through this and the next chapter, the rules for semi-colons become evident.

Indenting with spaces or tabs are optional but considered good programming style and
the editor will help enforce indents.

Run and test Program 1:1:

The program is ready to test and run.

A. Run the code by pressing the keyboard's F5 key (or pressing the green Start arrow on the
top tool bar).

Chapter 1 - The Editing Environment and First Program Page: 13


A new program window opens and displays on the Windows task bar - this is your first
C-sharp program. It is a fully-functional Windows program, complete with title-bars,
minimize and close buttons and the window can be resized and moved.

Button1 is functional, while button2 is not. Try the following:

Click button1;
label1 should change to "Cat".
button2 will do nothing as there is no code written for it.

B. Close the newly-run program by clicking the "X" in the upper corner; this closes the
program and returns to the editor, probably in code view.

For all of the examples in this book, it is assumed the test program is closed before
returning to design or code view. If you forget to do this, a lovely error message greets
you as you try to edit your program: "Changes are not allowed while code is running."

Build Errors:

Chapter 1 - The Editing Environment and First Program Page: 14


As you run and test, you may see this message if there is a bug in the program:
"There were build errors. Would you like to continue and run the last successful
build?"

1. For example, remove a semi-colon (after the variable assignment myFirstWordA =


Cat;). Note the error "(semi-colon) ; expected".

2. Press F5 (or click green Start button) to run the program.

Note "There were build errors. Would you like to continue and run the last
successful build?"

Click "Do not show this dialog again" and then "No". You never want to run the
"last successful" program – you always want to see the bugs so you can correct them.

Chapter 1 - The Editing Environment and First Program Page: 15


This option can and should be changed in this menu:

Tools, Options, "Projects and Solutions"


Choose "Build and Run"
Set to On Run "Do not Launch"

Comments on Program 1.1:

When "button1" was clicked in the running program (not in the editor), you initiated an
"event" (button1_click). The code within the opening and closing "squirrelly braces"
defined what happens. This block of code ran for the duration of the routine, up to the
closing brace.

The statement "string myFirstStringA;" creates a holding area (a variable) that stores
text-data – also called a "string". The name, myFirstStringA is an invented name.

Chapter 1 - The Editing Environment and First Program Page: 16


When declaring a string, integer or other type of variable, you are marking what kind of
data can live within that space – and C# requires all variables to be declared before they
can be used, making C# a 'tightly cast' language. Later chapters discuss the nuances of
variables and declarations.

Modify the Program to Work with Numbers:

For the next example, modify the program, changing all strings to numeric "integers".
The program will add the numbers together and display the results. Follow these steps:

1. Confirm the previously-running program was closed. If needed, click the "X" in the
upper corner or by ending the task on the Windows taskbar. This should return you to
the code-editor. Jump into Code View, when you return to the editor.

2. Scroll down the program code and locate the button1 event ("private void
button1_Click").

Make the following changes to the code (replacing all of the code originally typed). As
before, watch upper and lower cased letters, semicolons and braces:

• Between the opening and closing braces in button1's click event, delete all
statements previously written, but do not delete the braces. If the braces are
accidentally erased, re-type them.

• Create two "int" (integer) declarations (see code example, below), including
semicolons:

int valueA;
int valueB;

• On the next two statements, initialize (assign) each variable with a default value, in
the example, the numbers 3 and 2. (Integers can contain whole numbers, such as 0,
15, 99, -4, etc, but no fractions).

valueA = 3;
valueB = 2;

• Write a statement that changes 'label1.Text'. This command adds values A+B,
converts them to a string, then assigns the results to label1. Note in particular the
parenthesis, the period, and a closing semicolon.

label1.Text = Convert.ToString (valueA + valueB);

Chapter 1 - The Editing Environment and First Program Page: 17


Program 1.2; Working with Numbers, revised and completed
private void button1_Click(object sender, EventArgs e)
{
int valueA;
int valueB;

valueA = 3;
valueB = 2;

label1.Text = Convert.ToString(valueA + valueB);


}

By definition, a label (label1) is a string field.

3. Run the program by pressing F5. Click button1 to run the event.

Results: Label1 changes from it's default "label1" to "5" (3 + 2).

Comments:

The keyword "int" declares an integer (positive and negative whole numbers: 1, 2, 3, -55,
etc.). Be aware there are a half-dozen or so other numeric-type variables including
floating point, and these will be covered in later chapters.

The next line is more interesting:


label1.Text = Convert.ToString(valueA + valueB);

When reading a line of code like this, read it from the inside-out – starting at the inner-
most parenthesis:

• "(valueA + valueB)" are added together.

• "Convert.ToString (5)"
– the numeric values, once added, are converted to a string (think text).

• "label1.Text = "
Move (assign) the converted-to-string text to Label1.Text via an equal sign.

C# is a stickler about moving numbers and text around. If a variable is declared as an


integer (or the results of a calculation are integer), then the answer can only live in a
place where numbers are expected. Generally, you cannot move it to a Text field
without first converting.

If you've worked with Visual Basic before, you may know that VB implicitly converted
numerics to strings and it had little concern about moving numbers into text fields. C# is
not as lenient (especially versions before VS 2008). If you run afoul with this rule, you

Chapter 1 - The Editing Environment and First Program Page: 18


will get a compiler error (a compiler error is an error in the editor which shows before
you are allowed to run the program). This scenario is demonstrated next.

Modified with errors (implicit convert):

It is easy to forget the "Convert.ToString" when working with numbers. Simulate this
type of compiler error by doing the following:

1. If needed, stop the prior running program by clicking the "X in the upper right, returning
to the editing environment.

2. In code view (if needed, click on the top tab "Form1.cs" or by double-clicking button1).
Change the line:

"label1.Text...", removing the Convert.ToString( ) phrase, leaving this statement:

label1.Text = Convert.ToString(valueA + valueB);


label1.Text = valueA + valueB;

4. Press F5 and attempt to run the program. The compiler should immediately error with

"Cannot implicitly convert type 'int' to "string"

and the program will not run (see illustration, below, noting the Error List). If the Error
List does not appear at the bottom of the screen, choose top-menu, "View, Error List".

5. In the Error List window, double-click the error message text. This jumps the cursor to
the failed line in your program. Note the squiggly line.

Chapter 1 - The Editing Environment and First Program Page: 19


6. Correct the mis-typed statement by returning the "Convert.ToString( ...)" command, as
before.

Other ".text" Errors:

It is also easy to mis-type the 'dot-Text' after the label's name. The editor, which
normally helps while typing, can also automate the problem. Make this minor change in
your program:

1. Modify this line by replacing the .T (capital T) with a lowercase "t"

label1.text = Convert.ToString(valueA + valueB);

2. Run the program by pressing F5; note the compiler error:

'System.Windows.Forms.Label' does not contain a definition for 'text'...

Chapter 1 - The Editing Environment and First Program Page: 20


3. Again, double-click on the compiler error message and the editor jumps to the offending
line.

4. Finally, Visual Basic programmers often forget to type the ".Text" phrase all-together,
which was perfectly acceptable in that language, but not in C-Sharp. When you do this,
the compiler errors with:

"Cannot implicitly convert type 'string' to 'System.Windows.Forms.Label'

Which basically means the new string (Convert.ToString) does not have a string-variable
to arrive at – without the .Text property, C# refuses to play. Correct the error by
returning the word .Text to its proper case and position.

Comments on the "definition" error:

"label1" is an object added to the form, and like all objects in Visual C#, they have
properties. Properties can include things such as the font, where the label is on the
screen, and a myriad of others controls. In this case, the ".Text" (dot-Text) is a property
that describes what the label "says" on the form.

Since everything in C# is case-sensitive, including property names, mis-typing ".Text"


with a lower-cased "t" is tantamount to mis-spelling the keyword. Admittedly, the error
"System.Window.forms.Label does not contain a definition for 'text' " is not particularly
helpful.

As you were typing ".text", the editor offered help by suggesting a correct spelling, via a
feature called "Intellisense." In your program, try this by deleting and re-typing the .Text
phrase. As you partially type the keyword (".te" or ".tex"), arrow key to the correct entry
in the list and press the space-bar. The editor auto-completes the entry.

Chapter 1 - The Editing Environment and First Program Page: 21


However, there is some caution with this feature. A touch-typist who types a lower-
cased ".text", completing the word without using the menu pull-down, is stuck with the
lower-cased version, even if not intended. This will cause the error seen earlier and
sometimes these types of errors are troublesome to spot.

Chapter 1 - The Editing Environment and First Program Page: 22


Error Corrections

Program errors can be hard to locate. Here are some suggestions:

If the error makes no sense and line you were previously working on is flagged, check
the following:

• Is the current line, or the line directly above, missing a closing semicolon or closing
parenthesis?

• Do you have a closing semicolon on a line that is not supposed to have one?
In particular, these types of statements do not have semicolons at the end of their
lines:

if-statements,
loop-statements and
"private void..." declarations

For these statements, are opening and closing {braces} present? (These are covered
in detail in future chapters.)

• As you are typing "keywords," such as "dot-Text", does a pop-up intellisense


window appear? If not, the preceding variable name is likely misspelled or was not
declared or defined. Upper and lowercase is important.

• And probably the most common problem, are closing braces in the right position?
Especially near the end of the program. Be sure your code lives within an opening
anc closing-brace-pair – avoiding the last two in the program. Remember to click
behind a brace to see where its partner lives. They must match and must be lined up
properly.

The two braces at the very bottom of the program should never have anything typed
past them. They belong to these statements:

Chapter 1 - The Editing Environment and First Program Page: 23


• Although this is partially cosmetic (but important for easy-to-read code), confirm the
braces are indented properly.

"There were build Errors..." (Revisited)

Compiler errors may prompt with "There were build errors. Would you like to
continue with the last successful build?"

As a reminder, always select checkbox "Do not show again" and click No.

In other words, you would never want to run the previous version of your code (before
the new bugs were introduced – you really want to see the current bugs). I often wonder
why this is even offered as an option. If you accidentally click "[x] Do not show and
then Yes, you can fix this problem with Tools, Options, "Projects and Solutions", "Build
and Run". Set "On Run, when build or deployment errors occur" to "Do not Launch".

Chapter 1 - The Editing Environment and First Program Page: 24


"Form1 does not contain a definition for <button1_Click>..."

If you delete an object, such as button1 (or if you delete the underlying code and leave
the object on the Form), and try to run the program again, you may see this compiler
error: "Form1 does not contain a definition for "button1_Click..."

In Visual Studio, versions 2015 and older, this would cause a colossal crash. Double-
clicking the error will take you to a particularly-nasty-looking panel with a lot of code
you did not write. The highlighted line will be the culprit. Without fear, delete the line
and run the program again.

Starting Visual Studio 2017, the editor recovers more gracefully from these types of
deletes and it unobtrusively orphans the code and does not show an error.

Appendix A: Common Errors

Appendix A is a list of common errors and their solutions. The list is sorted
alphabetically by the error message's text. Most of the errors students will encounter
while using this book are listed. Starting with Visual Studio 2017, error messages and
warnings get a numeric code. Not all codes are listed; search by text.

Chapter 1 - The Editing Environment and First Program Page: 25


Variables and Scope

C# is always concerned about where a variable is defined and when it can be used. This
section attaches code to the unused button2 and demonstrates how variables are cleaned
up and discarded. The routine will attempt (and fail) to use the integers, valueA and
valueB, defined above in button1_Click. Basically, if a variable is declared in one
routine, it is not available or visible to other parts of the program. The correct lingo is
the variables "fell out of scope." This is best described with a short demonstration.

1. Return to the form's design view – see the top-row of tabs, clicking "Form1.cs [Design]".

If you see a message "Changes are not allowed while code is


running" or "Cannot currently modify...." then you have left
your previous example running. See the Windows task bar and
close the running program before returning to the editing
environment.

2 Double-click button2 (not button1). This creates a default event (button2_Click) for
that object.

Add this new code between button2's opening and closing brace. In this code
illustration, button1's previously-typed code is also shown. Button2's code may appear
above or below button1's statements; the order of the block (block of code) does not
matter.

As you type this new code, expect compiler errors in the editor's bottom Error List and
red-squiggly-lines and expect the editor to change what you have typed. Force these
editing changes into the module:

Program 1.4; Button 2, attempting to use valueA and B


private void button1_Click(object sender, EventArgs e)
{
int valueA;
int valueB;

valueA = 3;
valueB = 2;
label1.Text = ConvertToString (valueA = valueB);
}

//New Code for Button2 - this will fail

private void button2_Click(object sender, EventArgs e)


{
valueA = 3;
valueB = 4;

label1.Text = Convert.ToString(valueA + valueB);


}

Chapter 1 - The Editing Environment and First Program Page: 26


Important While typing "ValueA", the editor will use Intellisense and will
attempt to replace what you are typing with other values or
statements (this is a clear indication that something is wrong
with the variable name); force the editor to discard the
suggestions by pressing ESCape on the popup list, and keep
typing the text as illustrated:

The editor will be angry and a number of errors will show in the error list.

Run with Button 2:

3. Attempt to run the new code by pressing F5. Notice the compiler errors:

"The name 'valueA' does not exist in the current context."


"The name 'valueB' does not exist in the current context."

Chapter 1 - The Editing Environment and First Program Page: 27


4. Double-click on the first descriptive-text in the error message and notice it takes you to
button2's "valueA" statement.

Comments:

Although "int valueA;" was declared in the same program, it was declared within
button1. Because of that, the variables are not visible to other routines. A more
professional explanation is the "scope" of the variable was limited to button1. At
button1's closing brace, when button1 finished running, the variables are discarded and
are no longer available.

Variables with limited scope are double-edged. It is good that they are destroyed - this
leaves the program with a smaller memory footprint. It also keeps you from making
mistakes - especially in large programs where the same variable name ("myCounter")
might be used in different routines. Often, it is best when values die before they are
mistakenly used somewhere else. (Other chapters discuss variable scope and how to
make longer-lived variables)

Chapter 1 - The Editing Environment and First Program Page: 28


Continuing with button2:

5. Return to the form's design view (see "Form1.cs [Design]", along the top row of tabs).

6. From the form's design view, highlight button2, and press the keyboard's Delete key to
remove the object.

7. Double-click button1 to return to the code view (or use the Form1.cs tab). Scroll down
the program and note that button2's logic ("private void button2_Click") survived
the delete. This is normal. Deleting the object does not remove the associated code.
Outside of cluttering the program, there is no harm – provided the orphaned code does
not have errors.

Leaving button2's code in place, Press F5 again to re-run the program. You will get the
same compiler errors ("the name ValueA does not exist").

8. Return to code view and delete all statements dealing with button2; this includes the
everything from the "private void button2..." statement, through it's closing brace.
Press F5 to run. The program should run without errors.

Chapter 1 - The Editing Environment and First Program Page: 29


Working with Text Boxes

"textBoxes" are the data-entry fields seen in a Windows program. The boxes hold data
such as a name, phone number or address and the end-user tabs into the field and types.
The following examples introduce basic methods for building and using text boxes and
later chapters will cover this topic in greater detail.

Begin by starting a new C# program.

A. Discard any previous programs you have been working with.

From the editor, select File, "Close Solution".


(For this example, it is important to close the solution and start fresh.)

Visual Studio, version 2015 and older, will prompt to Save. If prompted, don't bother.

j Starting with Visual Studio 2017, it automatically saves, without prompting. If you
accidentally edited, and want to discard the changes, press Shift, Close Solution.

B. Select File, New Project,


As before, select "Windows Form Application."
Accept the default filenames.

C. On the newly-displayed blank form, create a similar program as before by clicking and
dragging the following objects from the ToolBox flyout menu onto the default Form1:

textBox1 (do not use Rich Text Boxes)


textBox2 (a second text box)
label1
button1

Double-click (or drag) each object from the ToolBox flyout and arrange them to look
something like the illustration below.

Chapter 1 - The Editing Environment and First Program Page: 30


As a reminder, TextBoxes are found in the ToolBox flyout, in "Common Controls."
Scroll down the list to find the control:

Chapter 1 - The Editing Environment and First Program Page: 31


If the Toolbox flyout menu does not have any options, showing "there are no
useable controls in this group...", you are probably in the code view tab.
Click the top-tab "Form1.cs[Design]".

Naming Fields in the Properties Window:

Each object has a variety of properties, including a "Name." To view an object's


properties, select the object by single-clicking, then "other-mouse-click" (right-click).
From the pop-up menu, select "Properties." Alternatively, select the top menu, View,
"Properties Window." A panel opens on the lower-right side of the editing screen. Once
opened, the pane remains open. A various objects are clicked, the details within the
properties window change. The object's Name is listed at the top of the Properties
window.

The object's name (e.g. textBox1) can be changed to any name of your choosing, but the
name must be a single word or phrase, without embedded spaces. For example, textBox1
could be renamed to "MyFirstDataEntryField", where most developers prefer to use
"CamelCasing," with initial caps on each word.

Most objects have an extensive list of properties and includes


such things as the size, location, visibility, and others. Most
developers leave the properties panel always open.

Chapter 1 - The Editing Environment and First Program Page: 32


I like to sort the properties alphabetically, as illustrated above.

Single-click each of the text boxes until you have identified "textBox1", again looking at
the property's name. Arrange the fields on your screen so it is the topmost box in your
design window.

Continuing work on Text Boxes: Concatenation

The goal of this example is simple: take two text strings and combine them into one new
string, displayed at label1. The program will start with two hard-coded values, "Cat" and
"Dog".

1. From the form's design view, double-click "button1" to jump into this object's code
view.

2. Add this statement inside of button1's _Click event. As before, the code lives between
the opening and closing braces and, as always, watch upper and lowercase. End the
statement with a required semicolon.

Program 1.5, Concatenation


private void button1_Click(object sender, EventArgs e)
{
label1.Text = "Cat" + "Dog";
}

In the code-editor, it will look like this:

Chapter 1 - The Editing Environment and First Program Page: 33


3. Run the program by pressing F5.
Click Button1.
Results: "CatDog" appears at label1.

Because the string literals "Cat" and "Dog" were hard-coded, the two textBox fields
were not used in this part of the example.

Comments on "Adding two text strings":

The line
label1.Text = "Cat" + "Dog";

instructs the compiler to concatenate two literal strings and place the result into
label1's .Text. Except for the fact that you "added" two strings together using a plus-
symbol, this shouldn't be too big of a surprise. (VB and Excel programmers always used
"&" for string concatenation, C# uses a plus.)

Chapter 1 - The Editing Environment and First Program Page: 34


4. Next, replace the hard-coded string literals with a reference to the text boxes. Replace
"dog" and "cat" with this statement – but note this is a flawed line of code:

label1.Text = textBox1 + textBox2; //Flawed

5. Press F5 to run and you'll get this error:


"Operator '+' cannot be applied to operands of type 'TextBox' and 'TextBox' "

The code seems reasonable and on the surface it should work. The problem is this: C#
never works on an object's name by itself – it always needs to manipulate what I call a
'dot-property.' For VB programmers this is a constant surprise because VB assumed
what was not explicitly stated.

6. Re-write the line to look like this:

label1.Text = textBox1.Text + textBox2.Text; //Corrected

Before any value in a textBox can be used, you must explicitly state the .Text property,
as in "textBox1.Text". C# is persnickety in this regard.

Testing:

Run the program (F5)


Type any short text string in textBox1 and in textBox2.
Click button1 to see the results. The two text box values will concatenate at Label1.
Close the program and return to design view.

Chapter 1 - The Editing Environment and First Program Page: 35


Default Text Values:

In design view (where the Form's Design is visible; see top-tab-bar "Form1.cs[Design]"),
single-click textBox1 to highlight the object. In the Properties window, lower-right
corner of the editing screen, scroll down the list. Note properties such as Background
Color, Border Styles. Near the bottom of the list, find the ".Text" property.

The ".Text" is the same ".Text" used earlier and is where the value of this object lives.
From design view, this property's default value can be pre-populated and the end user
can change at run time. Both the design and the user's view change the same value.

1. If you haven't already done so, close the previously running program by clicking the "X".
Return to the Form's design view (see the top-row tab: "Form1.cs [Design]")

2. Click textBox1 to select.


In the Properties window, scroll down the list, locating the "Text" property.

Type the word "Angry".


Similarly, in textBox2's Text Property, type "Beavers".
F5 to run the program.

Chapter 1 - The Editing Environment and First Program Page: 36


Results: TextBox1 and 2 are pre-populated. Clicking button1 combines them into one
value, "AngyBeavers" at Label1. Change the text in the textBoxes and click button1
again; note how it adjusts to the new values.

Locking a field with "Enable / Disable":

Using the same properties list, lock a field so users cannot edit - forcing them to accept
the defaults.

3. Return to design view


Highlight textBox2.
Locate the "Enabled" property.
Double-click the property's value to toggle between True/False, choosing False.

4. Run the program again.


Results: The second textBox is disabled and you are unable to edit the field.

Combining textBox Values and Literals:

As a last example, combine the two textBoxes with a third literal, adding the word
" and " between "AngryBeavers".

5. Modify button1's click event by adding the literal " and " between the two textBox
values. Double-click button1 to open Code View:

Chapter 1 - The Editing Environment and First Program Page: 37


label1.Text = textBox1.Text + " and " + textBox2.Text;

To make this look proper the " and " needs leading and trailing spaces between the
quotes. Without the spaces, the result would be "AngryandBeavers".

This concludes this introduction to C#'s editing environment and a basic textBoxes. The
next chapters go into greater detail, including how to audit and limit what types of data
users can type.

By now you should have a general feeling for how the editor works and how to read and
interpret various compiler errors that we all accidentally type while programming. The
next chapter continues with variables and loops.

Chapter 1 - The Editing Environment and First Program Page: 38


Exercises
Most chapters in this book contain exercises and they are meant to be challenging.
However, this chapter will guide you with step-by-step hints. You are encouraged to do
all exercises to re-enforce the topics.

Exercise A

Create a program that prompts the user for their first name, mid-name, and their last
name. Use button1 to combine the three names into one string and display the results in
a label. For example, the user may type "John", "Q." and "Smith", with the final results,
"John Q. Smith". Label the fields so they look pretty.

Attempt this now, before following the steps.

A. Start a new Project, with a Form1.

B. Using the Toolbox flyout, drop three textBox fields, textBox1, textBox2, and textBox3
onto the form.

C. Change each textBox's Name property, from textBox1 to a more appropriate variable
name:

FirstName
MidName
LastName

(Variable names cannot/should not have spaces in them)

The "Name" property is not visible to end-users

Chapter 1 - The Editing Environment and First Program Page: 39


D. Drop three cosmetic labels on the form, label1, label2, label3 and change their text
property to read "My First Name", etc., as illustrated above.

Note: Labels also have a NAME property, which is different


than their .Text property. With the Name, think "variable
name."

If the label is strictly cosmetic, leaving them named as label1,


label2, is acceptable. If you had logic attached to them, then
they should be re-named – but you could not use "FirstName"
because that is already taken by the textBox. A name, such as
"lblFirstName" would be more appropriate.

E. Add another label, "label4", leaving it with its default name and default text value. This
will hold the answer, as generated by button1.

F. Using the toolBox flyout, add Button1.

Double-click the newly-dropped button and create an on-click event that assembles the
three text fields and places the results in Label4. For example, "John Q. Smith". When
doing this, how can you avoid this result: "JohnQ.Smith"?

Exercise B

Select File, "Close Solution" (and if prompted, save all).

From the Visual Studio Getting Started page, note the Recent Section.
Ignoring the Recent section, confirm you can find and re-open the project/Solution
manually by clicking File, "Open Project/Solution"

Confirm you can find and re-open the project.

A. Select File, "Close Solution" (and if prompted, Save all).

B. From the Visual Studio "Getting Started" landing page, note the Recent section.

Open the Project/Solution Manually:

C. Ignoring the Recent section, Select File, "Open Project/Solution"

Browse to (this default location)


"This PC \ Documents \ Visual Studio 2017 \ Projects"
Double-click folder (WindowsFormApp2) (or most recent)
Double-click "WindowsFormsApp2.sln" (solution file)

Chapter 1 - The Editing Environment and First Program Page: 40


In general, always open the Solution file. Do not open .cs, .resx,
or other type files. The "solution" contains everything about
your project, including all forms and code, all in one package.

D. Once the solution opens, look in the Solution Explorer pane (top-right side of editing
screen). "Other-mouse-click" Form1.cs, choosing "View Designer" or "View Code".

Later chapters discuss better places to save your projects than the defaults
Microsoft chooses.

Chapter 1 - The Editing Environment and First Program Page: 41


Chapter 2 - Introduction to Loops

Loops tell the computer to repeat a group of instructions multiple times. For example, a
program may need to step through each record in a file or it may need to loop through a
string, looking for a particular set of characters. There are several different types of
loops and while they may seem interchangeable, each is designed for slightly different
needs.

Probably the two most useful loops are "while-loops," looping while a condition is true
and for-next loops, which loops a pre-determined number of times. There are also for-
each loops (processing each element in an array) and nested loops. This chapter
demonstrates each and a solid understanding of the topic is important.

Topics:

• "while" loops
• incrementing with <loopCounter>++
• MessageBox.Show
• Infinite Loops
• ctrl-alt-Break debugging
• scrollbars
• "do" loops
• for-next loops
• Controlling loops with textBoxes
• Interrupting loops with "continue" and "break"
• Nested loops

Calling a loop recursively - advanced loop: See Chapter 20 - Printing

The loops demonstrated here are simple: print your name ten
times, build geometric shapes and other nonsensical things, but
don't loose track of the goal – learning how loops operate. For
this chapter, there was no need to make the loops any more
complicated. Later chapters make extensive use of these
constructs.

Chapter 2 - Loops Page: 45


while loop, Overview
int iloopCounter = 0;

while (iloopCounter <= 10)


{
//May not execute, depending on loopCounter value...

iloopCounter++; //You must increment the loop counter!


}

All loops can use these Early Exits:


continue; //re-loop (but watch increments in while and do-loops)
break; //end loop immediately

do-loop, Overview
int iloopCounter = 0;

do
{
//Always executes at least one time;
//note semicolon on while-statement

loopCounter++; //You must increment the loopCounter!

} while (loopCounter <= 10);

for-loop (for-next), Overview


//Counting Forward from 1 to 10

for (int iloopCounter = 1; iloopCounter <= 10; iloopCounter++)


{
//Do stuff here
//No loopCounter statement needed inside the loop
}

//Looping backupwards, 10 to 1:

for (int iloopCounter = 10; iloopCounter >= 1; iloopCounter–)


{

Chapter 2 - Loops Page: 46


foreach (array) loop, Overview
//String array must be pre-defined

foreach(string strtempString in astringArray)


{

//Useful with arrays only; see Chapter 22

//Can only use the temp variable within the loop


//No loopCounter or conditional is needed
}

Building the Example Program:

Begin the first loop exercise by building a new project in the same manner as the
previous chapter and this same project can be used for all examples in the chapter.

A. Begin by discarding any previous programs.


Select File, "Close Solution". Do not bother saving previous examples.

B. From the Visual Studio top menu, select File, New Project,
Select "Windows Form Application"
Accept default filenames, etc.
(See the previous chapter for illustrations)

C. From the ToolBox flyout (Common Controls), drag or double-click these objects to the
new Form window.

textBox1
textBox2
button1

D. Locate textBox1 (by clicking each textBox and looking in the Properties pane for the
object's name).

Convert textBox1 to a Multi-line box:

• Click the textBox object one time to highlight, then scrolling through the Properties
window (on the lower-right of the screen), set the "Multiline" property to True. (If
you don't see the Properties panel in the lower-right corner of your screen, see menu
choice View, "Properties Window")

• Alternately, set the Multi-line property graphically: Click the textBox, then click the
flyout menu-arrow, checking [x] MultiLine:

Chapter 2 - Loops Page: 47


E. Still in design-view (confirm top-tab "Form1.cs [Design]"), highlight the form's
background and resize the form, and textBox1 by dragging its selection handles until it
looks similar to the illustration below: (Before you can stretch a field vertically, it must
be set to Multi-line).

Re-arrange/move the fields and buttons until the screen looks similar to this illustration.
You may need to resize the form and move fields to make room. Resize the form by
dragging the form's handles (illustrated). To move fields and buttons, click and drag.

The program is now ready to accept code.

Chapter 2 - Loops Page: 48


"while" Loops

"while" loops are probably the most common and most useful looping command. They
have the following characteristics:

• Loop "while a condition" is true.


• The number of iterations do not need to be known before starting the loop.
• Checks for the "condition" before doing the work.
• Requires initial variable setup and house-keeping.
• Can easily and accidentally become an "infinite" loop.

Imagine this loop: "while money in wallet go shopping; if there is no money, don't go
shopping." This is the key feature of the loop - check before running. There is a
possibility the loop does not execute.

"while" loops require housekeeping. You, as the developer,


provides and nurtures a variable that controls the loop. The
examples will use a newly-created variable called
"loopCounter". There is nothing special about the name – any
made-up name will do. How variables are declared are covered
in more detail in the next chapters but for now, just follow along
in the examples.

The first example will loop 10 times, printing some text each time. Later examples
expand this idea by introducing counters and line-breaks.

while Loop Example with loopCounter:

Write an admittedly pointless loop that prints the word "hi" 10 times.

Program 2.1

1. From design view (built on the previous page), double-click button1, opening button1's
code-view.

2. Starting after "button1_Click"'s opening brace, declare an integer variable


"loopCounter", and assign an initial value. This is the variable that tracks where you are
in the "while" loop. Statements that begin with "//" are comments and are ignored by
the program.

private void button1_Click(object sender, EventArgs e)


{
int loopCounter;
loopCounter = 1;

//The while-loop will go here...


}

When writing these statements, be sure the statements are typed between button1's
opening and closing braces. And notice how the two statements end in semi-colons.

Chapter 2 - Loops Page: 49


Below this paragraph's closing brace are two additional closing braces that don't belong
to this routine; ignore them for now.

3. The actual loop is written at the //comment's location.

Begin the loop with the keyword "while", typed with a lower-cased "w", followed by a
parenthesis and a conditional-statement that in English reads, "while loopCounter is less
than or equal to 10."

Notice after the closing parenthesis, there is *not* a semi-colon.

Beneath the while loop is a new set of opening and closing braces, which you must
manually type. This is where the loop's interior statements will live. The blank line
between the variable declarations and the loop is cosmetic.

private void button1_Click(object sender, EventArgs e)


{
int loopCounter;
loopCounter = 1;

while (loopCounter <= 10)


{

}
}

Complete the routine, by typing the two statements inside the loop and a MessageBox
statement after the loop's closing brace, but within the button1_Click's closing brace.
The position of these statements is important:

Program 2.1 - Basic "while" loop, complete


private void button1_Click(object sender, EventArgs e)
{
int loopCounter;
loopCounter = 1;

while (loopCounter <= 10)


{
textBox1.Text = textBox1.Text + "hi";
loopCounter = loopCounter + 1;
}

MessageBox.Show("Done");
}

where:

• A detailed explanation of the statements follows in the next several pages.

Chapter 2 - Loops Page: 50


• Pay attention to semicolons and upper and lowercased names. Almost all statements
in C# end with a semi-colon, but the while-loop line does not.

• Stylistically, indent your code with either spaces or tabs; The editor automatically
helps with indenting.

• As you type, count the opening and closing braces to make sure that each brace has a
matched pair.

When typing braces, type the opening and closing braces, one after the other,
before typing the code between. Put a couple of blank lines between, giving
yourself room for the interior statements. Typing the braces in this fashion
helps you keep track of their position. Starting in VS2013, typing the
opening brace, the editor automatically types the closing brace.

Many beginning programmers get the opening and closing braces confused with the
other braces in the routine. It is important they are paired properly. Although the
compiler does not particularly care about indentation, you can get confused about where
the closing braces belong when the formatting is poorly maintained. When typing the
while-statement, I recommend typing it in this fashion in order to keep the braces paired:

Type the "while (loopCounter <= 10)" (no semi-colon); Press Enter,
Type the opening brace, {
Press Enter, Enter (twice)
Type the closing brace, }

with results that look like this:

while (loopCounter <= 10)


{

Starting with Visual Studio 2013, the editor automatically types


the closing brace when an opening brace is typed.

Notice the while-clause does not have an ending semicolon


[while (loopCounter <= 10)] this is required by the syntax. Technically,
the next pair of opening and closing braces act as a semicolon - or if you will,
a end-of-statement delimiter. This is similar to the procedure's name
[private void button1....], which also does not have a closing semicolon
because of its braces.

Finding a brace's matching pair:

After typing the opening and closing brace, place the cursor after either brace and the
editor will highlight the other matching brace. If indented properly, the braces will be
easy to interpret.

Chapter 2 - Loops Page: 51


Make sure the loop and its braces stays above button1_Click's closing brace. Other
braces exist below this routine; leave them unmolested.

Some developers code their braces in this fashion, which is also acceptable:

while (loopCounter <= 10) {


textBox1.Text = textBox1.Text + "hi";
loopCounter = loopCounter + 1;
}

Testing the Program:

A. Once the loop is written, run the program by pressing F5.

B. Click button1 to execute the loop.

Results: textBox1 shows "hihihihihihihihihi" along with a simple dialogue "Done". Note
10 concatenated strings.

C. Click OK on the MessageBox "done".


Close the program ("X") and return to the editor.

Chapter 2 - Loops Page: 52


Comments on the while-loop:

There are several new concepts in this program. Starting at the top, a variable,
"loopCounter" is declared as an integer (a whole-number, counting variable), followed
by a statement that initializes a default value 1.

Of interest, the computer allocates memory for the variable


when initialized - not when declared. Also, if a variable is
declared but not initialized, the compiler complains. There is an
example of this in a few pages.

Many C# programmers prefer to declare and initialize on the same line, using a
condensed syntax. Either style is acceptable.

int loopCounter = 1;

vs
int loopCounter;
loopCounter = 1;

There is nothing special about the name "loopCounter" – this is an invented name. But
no matter what name is used, remember C# is case-sensitive. If you spell "loopCounter"
with a lowercased-el and a capital-C, you must use that same scheme throughout the
module.

j Variable names must be single-words or phrases with no embedded spaces. By


convention, most programmers use CamelCase phrases, where words, begins with a
capital letter.

Chapter 2 - Loops Page: 53


Using punctuation such as "Loop_Counter" or "Loop-Counter" (underscores,
hyphens) is considered poor technique and you will be looked-down-upon – mainly
because programmers are to lazy to type the dashes and underscores. Different
shops have different standards in this area.

By convention, I like to type a variable's first letter as lower-


cased, indicating the variable is a "local" variable, defined in
this function (as opposed Form-level or Global variable). These
ideas are described in future chapters.

Definitions

How a variable is cased is known by two names.

PascalCasing where the first letter of each word is capitalized. This is


used for function (Method) names and for variables
outside the scope of the current routine.

camelCasing Where the first letter of the first word is lower-cased and
all other words are uppercased. This is usually used for
variables within the current routine.

Additionally, you will see variables named in "Hungarian Notation",


where the variable is prefixed with what type of data is stored in the
variable. For example:

strPersonsName A string variable


iloopCounter Integer

Hungarian Notation is now frowned up, but is used in this book to help
illustrate and clarify the variable's purpose.

The main "while" statement:

Examine the while-statement for a moment. "while" <a condition is true> do all the
"stuff" between the next pair of braces. If the condition were initially false, none of the
code within the braces executes and program passes control to the line after the closing
brace, which in this case, is the MessageBox.

In this example, "loop while the counter is less-than or equal-to 10" is guaranteed to run
because the variable was initialized to one.

Chapter 2 - Loops Page: 54


In order for the loop to work properly, something inside the loop must change
the condition – in other words, loopCounter must increment, approach, then
pass 10 or the loop never ends.

There are other ways of making this loop run ten times. You could, for instance, do any
of the following, all with the same results:

initialize loopCounter =1 and run until <= 10 (as illustrated)


initialize loopCounter =1 and run until < 11 (less than)
initialize loopCounter = 0 and run until <= 9 (less than or equal to)

As the while-loop executes, it processes the statements within the braces.


When it reaches the closing brace, it loops back to the top and examines the
condition to see if it should continue.

Appending a String:

The example prints the word "hi" ten times in textBox1, one after the other. To
accomplish this, the code relied on a somewhat oddly-worded programming trick:

textBox1.Text = textBox1.Text + "hi";

The statement says:

• "Take what ever is currently in textBox1 and add the word 'hi'". With strings,
'add' means append or concatenate.

• When the loop first starts, textBox1 is empty so the first iteration concatenates
"hi" to an empty box.

• On the loop's second iteration, the box contain the first loop's "hi", then another
copy is concatenated. Remember, "take what ever is in textBox1 and add a new
"hi" – putting the results back into textBox1.

• On the loop's second run, textBox1 will contain "hihi".

After ten iterations, the loop will have appended ten "hi"'s, giving: "hihihihihihihihihihi".

Chapter 2 - Loops Page: 55


You can make this nonsensical output easier to read by inserting a space at the end of the
literal "hi", resulting in "hi hi hi hi...".

Incrementing a Counter:

The loopCounter statement uses the same trick, except with a numeric value:

loopCounter = loopCounter + 1;

The counter was initialized at 1 and then, on the first iteration, a "+1" is added the
current value (one), giving a total 2. On the next loop, the current value equals two plus
one, for three. The statement says, "take what ever the current value is, add one, and put
the results back into the current value." Mentally you can follow the variable's progress
through the loop.

loopCounter equals 1 as you enter the loop


"hi " prints for the first time
then the loopCounter, which still equals 1, is incremented: 1 + 1 = 2
Loop the next iteration

At Loop #2,
"hi " + "hi " is printed
loopCounter = 2; add + 1, for a total of 3
Print "hi hi " + "hi "

At Loop #3,
loopCounter = 3; add 1 (loopCounter = loopCounter + 1) = 4
"hi hi hi " + "hi " ... etc.

The syntax is admittedly strange.

What Would Happen if...:

= textBox1.text + "hi " may seem like unnecessary gibberish. Why not use a more
straight-forward statement, such as: textBox1.Text = "hi"; Try this now.

With this change, the computer still prints the word "hi" 10 times, but each iteration
replaces the previous – it did not append or concatenate. The computer takes the
instruction literally and the revised command says to 'Put "hi" in textBox1.' This runs
ten times with each "hi" replacing the earlier ones. There would be no consideration on
the previous contents and all ten copies happen in a millisecond – too fast to see. The
end result would be a single "hi" in the box, the tenth iteration.

Chapter 2 - Loops Page: 56


More importantly, if the loopCounter were written in the same way (e.g.
"loopCounter = +1"), it would have the same problem. Each iteration would put a "+1"
into the counter and it would never grow beyond 1 – no matter how many times the loop
ran. Since it would never reach 10, it would be an infinite loop (don't do this yet; you
can practice an infinite loop in a few pages).

Ending the Loop:

Here is the key to the loop: Ultimately, the loop enters its 10th iteration - where it prints
"hi" a 10th time. The counter increments to 11 and wraps around to the top, where the
"while" condition again checks the current value. Since it is now at 11, the loop ends
and program control falls to the statement after the closing brace.

Incrementing with "++":

The traditional way add +1 to a variable is:


loopCounter = loopCounter + 1;

however, most C# programmers use a syntactically-shorter version that performs the


same task and mentally reads the same way as the longer version.

++loopCounter; or
loopCounter++;

Using either a "++" in front or behind the variable accomplishes the same goal.
(Technically, if the ++ is in front, it adds one to the variable before using the variable in
other inline calculations. If placed behind++, it adds one after using the value in the
other calculations. This is subtle and in practice, there is little difference between the

Chapter 2 - Loops Page: 57


two, especially if the statement is a stand-alone statement on a single line of code. Most
programmers use the trailing++ version.)

This new syntax will cause problems if both styles are mixed on the same line. For
example, this statement is an infinite loop:

loopCounter = loopCounter++; //Do not do this

Instead:
loopcounter++; or
loopCounter = loopCounter + 1;

As an unimportant, trivial fact, in the bad example, the existing loopCounter++ (value
equals "1") is assigned to loopCounter before the +1 is added. Flipping the order of the
"++" to front of the statement would work properly, although this is non-standard and not
recommended.

Counting by two's:

"++" is the recommended method because this is the style most commonly used and is
slightly more efficient under the hood. But the older style is still needed. Consider a
loop that counts by twos: 2, 4, 6, 8, 10. The "++" method does not work and you must
use the longer style. This grows the counter by two, printing "hi" five times:

private void button1_Click(object sender, EventArgs e)


{
int loopCounter;
loopCounter = 2;

while (loopCounter <= 10)


{
textBox1.Text = textBox1.Text + "hi";
loopCounter = loopCounter + 2;
}

MessageBox.Show("Done");
}

Concatenating to Self with "+=":

The statement that appended "hi " to the existing textBox can also be abbreviated. Since
adding something to itself is an amazingly common operation, C# has a more concise
syntax that is favored by most experienced programmers.

Instead of
textBox1.Text = textBox1.Text + "hi ";

use this syntax, with a plus-equals:


textBox1.Text += "hi ";

Chapter 2 - Loops Page: 58


Which basically says "append the quoted string back over the top of the previous value."
Both statements accomplish the same goal.

The Counter's Position:

The position of the loopCounter-increment is important. In a "while" loop, it is always


placed near the end of the loop, usually directly above the loop's closing brace. If the
loop were incremented at the top of the loop, "loopCounter" jumps to 2 before it had a
chance to do any work with the previous value.

while (loopCounter <= 10)


{
<do stuff here>
<do more stuff>

loopCounter++; //increment at the end


}

Loop Increment Position

Common Mistakes when Typing while-loops:

As you study the program above, it probably looks simple, but I've found most beginning
programmers manage to get the brace-positions confused. If a closing brace is missing
or mis-placed, the compiler displays all kinds of strange and hard-to-figure-out error
messages.

You may have typed a semi-colon after the while statement, resulting in a compiler error,
"Possible mistaken empty statement".

Chapter 2 - Loops Page: 59


while (loopCounter <= 10) ; <Bad semicolon
{
:

Consider this loop problem, where the intent is to loop until strfoundString contains
some value other than an empty or null string (e.g. loop until string is empty). What is
wrong with this statement?

while (strfoundString = "")


{

with a compiler error: "Cannot implicitly convert type 'string' to 'bool'?

Answer: A single equal-sign is an assignment; use double-equals (==) in if-


statements: while (strfoundString == "").

Other Bad Code Examples:

Examine this code for a moment (or try it yourself).


Note how the MessageBox statement is outside of the button1_Click routine:

If code lives outside of the boundaries of it's routine, the compiler panics with a bizarre
"invalid token '(' in class, struct, or interface member" error. The highlighted line, which
has perfectly good syntax, will continue to complain as long as it is mis-positioned.

Chapter 2 - Loops Page: 60


Try this: What happens if the MessageBox statement is placed at the bottom of the
while-loop, just above the while-loop's closing brace, but still within the
module's closing brace?

Horrible Indenting Example:

In classes I have taught, beginning students don't always keep their braces typed in a
logical order and they don't always indent properly. Examine this code and decide if the
routine will work. The last two closing braces correctly belong to class and namespace
definitions higher-up in the program.

This routine works but it is difficult for humans to interpret. Indentation matters.

Chapter 2 - Loops Page: 61


Diagnostic MessageBoxes:

The computer can help you understand how a loop increments. Looking below at
Program 2.1b, line #10, add the new MessageBox statement. By inserting a second
"MessageBox.Show", you get what I like to call a "diagnostic MessageBox" (do not type
the line numbers).

Program 2.1b - Looping 1-10, modified


1 private void button1_Click(object sender, EventArgs e)
2 {
3 int loopCounter;
4 loopCounter=1;
5
6 while (loopCounter <= 10)
7 {
8 textBox1.Text = textBox1.Text + "hi ";
9
10 MessageBox.Show("Loop Counter = " +
Convert.ToString(loopCounter));
11
12 loopCounter = loopCounter + 1;
13 }
14 MessageBox.Show("Done");
15 }

You can type the statement as one line of code:

MessageBox.Show("Loop Counter = " + Convert.ToString(loopCounter));

or as one statement, typed on two physical lines, for cosmetic or style reasons:

MessageBox.Show
("Loop Counter = " + Convert.ToString(loopCounter));

or as:

MessageBox.Show
("Loop Counter =
" + Convert.ToString(loopCounter));

C# does not care about "white space." The closing semi-colon marks the real end of the
statement. However, you cannot break a quoted (literal) string in this fashion.

VB programmers will recall the _underscore was the line-


continuation character; C# does not need a line-continuation –
but does need a line-ending character (";")

Depending where you put the MessageBox, either above the increment or below, you'll
get differing results on loopCounter's status. For example, the MessageBox at line 10
shows the loopCounter at 10 just as the loop ends, but if the MessageBox were moved to
line 12a it would show 11. Press F5 to try it out.

Chapter 2 - Loops Page: 62


More on the MessageBox:

As you have probably surmised, MessageBox displays a simple dialogue box. The
example contains two MessageBoxes: One shows what the loopCounter equals inside
the loop and the second announces "Done."

The syntax of the MessageBox command should be evident. You might consider
"Messagebox" as a 'keyword' but that isn't strictly true. Amazingly, C# has only a few
dozen reserved keywords and MessageBox isn't one of them. In reality,"MessageBox" is
a "class" (of instructions) and ".Show" is one of the "methods" available to the class. As
you learn more about C#, these distinctions will become clear.

While testing and debugging code, I often use MessageBoxes to


peek inside of variables. It is a quick way to pop something up
on the screen and it doesn't require fields or labels. Later, more
powerful debugging tools are discussed.

Editing Concerns:

C# does not allow editing while the program is running and you will see problems,
especially if a MessageBox is waiting for you to click "OK". Run the example program
from above, but leave the "Done" Messagebox on the screen. Return to the editor and
attempt to change the program. The editor displays a clear message, "Changes are not
allowed while code is running" (Visual Studio 2010 and older: "Cannot currently modify
this text in the editor. It is read-only")

In the Form Designer's view, the indicators are more subtle. The ToolBox flyout is
either missing (or empty in VS2008, with a disturbing "there are no useable controls in
this group." ) You will also see a red-square ("stop") on the top toolbar as well as "lock"
symbols on the tabbed list.

Chapter 2 - Loops Page: 63


Stop the program by closing the MessageBox, then clicking the running program's red X,
or by clicking the Stop Button on the tool bar (which is a less-than-graceful hard-stop).

Chapter 2 - Loops Page: 64


Infinite Loops

No discussion about loops is complete without a glance at "infinite loops." Technically


this is a loop that never reaches a "false" condition. Or to word it differently, the while-
loop is always "true" and thus never ends.

Infinite loops are surprisingly easy to write, especially by accident, and the most
common problem is when you forget to increment the loop counter. For example, in the
programs above, if you neglected to grow the loopCounter, the variable never reaches 10
and the loop never ends. In this part of the chapter, you will explore how to make an
infinite loop and how to escape from them.

Writing an Infinite Loop:

There is no harm in writing an infinite loop, but it does get weird. Read this section
before starting the loop so you will know what to expect.

Modify Program 2.1b by doing the following:

A. Delete or comment-out (using slash-slash) the loop-increment statement at line 10. You
may have used "loopCounter++":

// loopCounter = loopCounter + 1; or
// loopCounter++;

B. For the first test, add the diagnostic MessageBox at line 10, just before the now-removed
loopCounter increment. This will be used to explore the concept:

:
6 while (loopCounter <= 10)
7 {
8 textBox1.Text = textBox1.Text + "hi ";
9
10 MessageBox.Show("Loop Counter = " +
Convert.ToString(loopCounter));
11
12 // loopCounter = loopCounter + 1;
13 }
:

C. Press F5 to run the program.


Click button1 to begin the loop.

Because the loopCounter was removed, the variable remains at 1 and never "grows."
The program is in an infinite loop, tempered by the MessageBox. You will be pestered
by MessageBoxes. Click OK a few times to get a feel for the loop.

Chapter 2 - Loops Page: 65


Breaking into the Loop:

There are several ways to break the loop. When you 'break" the loop, the editor
highlights the line(s) in the program where it is looping, giving a clue on how to fix the
problem. When breaking into the loop, you are in "debugging mode:" Clicking the tool
bar's "red-square" completely stops the running program and does not enter into the
debugging (break) mode.

Ignore the running program and its diagnostic MessageBox for a moment. Click
anywhere inside of the editor's code view window. This activates the editor, bringing it
to the foreground. You must do this first in order to enter debugging mode.

While the program is still running, and while you have the editor active, do one of the
following:

• Press ctrl-alt-Break (the keyboard's PAUSE key); this is not a "Ctrl-Break".

• Alternately, click the toolbar's Pause button (illustrated).

• Alternately, click inside of the editor's workspace (again, ignoring the looping
program). From the top menu, select Debug, "Break All".

Chapter 2 - Loops Page: 66


When the program breaks, the editor highlights where the program's current command
was running – in this case, it is the diagnostic MessageBox statement, but in a real
infinite loop it would probably be the "while statement." Notice your program (Form1)
is still running on the task bar. See below for additional instructions.

Diagnostics in Break Mode:

While in a debugging mode (break), hover the mouse over any variable name (especially
loopCounter) to see its current value. In this example, notice how loopCounter is always
"1" – even though you may have been through the loop a thousand times. This is the
very definition of an infinite loop.

Once in a break/debugging mode, press the toolbar's green "Continue" arrow to jump
back into the program, as-if the break hadn't occurred (in this case it would simply loop
around and stop again at the same MessageBox statement).

Ending the Program:

Although the editor and the debugger may have pushed the program into the background,
the program is still running. You will see "Form1" in the Windows task-bar. End the
program using any of the following methods:

Method 1: Bring the running program (taskbar, "Form1") to the foreground by clicking
on the taskbar icon. Once the Form appears, click the close-X. Be aware
this part of the interface can be disabled, so this option is not always

Chapter 2 - Loops Page: 67


available and be aware the MessageBox may prevent you from closing the
underlying program.

Method 2: Click the "red-square" icon on the toolbar.

Method 3: On the taskbar, look for the running program, labeled as "Form1" (not the
Visual Studio icon). Other-mouse-click the "Form1" taskbar button and
select Close. Windows will prompt to "end task".

You will likely be bugged about sending an error report to Microsoft. As humorous as
this sounds, click "Don't Send." See the Appendices (Common Error Messages) for
information on how to disable this prompt.

The Second Infinite Loop Test:

For a second more realistic test, return to the program editor and remove the diagnostic
MessageBox. When you do this, breaking into the program's debug mode is more
challenging.

A. Remove the diagnostic MessageBox, at line 10.


Confirm the loopCounter increment is still deleted or commented.

Chapter 2 - Loops Page: 68


B. Press F5 to run the program again.
Click button1 to start the loop.
This time there will be no dialog boxes to pester you and surprisingly, there won't be any
screen activity.

C. Wait a few seconds and click inside the running program.

Notice there is no screen activity!


Interestingly, the textBox does not fill up with a bunch of "hi"'s.

You can't move the running program's title bar.


The title bar may show "not responding".
Finally, if inclined, Windows task Manager shows CPU utilization at 25 to100%,
depending on the number of CPU's.

• Break the program by clicking inside of the editor and pressing ctrl-alt-Break (the
pause toolbar) and hover over the loop-counter variable, noting it stuck at 1.

• Click the toolbar's red-square (Shift-F5) to stop the program. Do this before the next
editing step.

Why the Display Does not Update:

You may be asking why textBox1 doesn't fill up with an infinite number of "hi"'s? The
computer is so busy looping that it doesn't have time to re-paint the screen.
(After stopping the program), you can force the screen to re-paint with a Refresh
statement:

Chapter 2 - Loops Page: 69


:
while (loopCounter <= 10)
{
textBox1.Text = textBox1.Text + "hi ";
this.Refresh();

// loopCounter = loopCounter + 1;
}
:

where:

• "this" refers to the currently-opened Form and it begins with a lower-case 't'. Visual
Basic and Microsoft Access programmers may recall the similar "Me" prefix.

• Refresh ( )'s parenthesis indicate the "Refresh" is a method of the "this" class.
Parenthesis are always required after a method name.

Try running the program again. Results:

Ultimately the program will crash when the textstring or textBox control exceeds limits.
Windows Task Manager will also report "Program not responding" even if the loop were
still active because it would not respond to other mouse and keyboard events. Ctrl-alt-
Break into, and stop the running program.

You now know how to identify and break out of a loop.

Chapter 2 - Loops Page: 70


"while" Loop - Printing Numbers 1-10

Modify the example program, (Program 2.1), so it prints the numbers 1 through 10
(instead of "hi"). You should have enough knowledge to complete this task and you are
encouraged to try this before reading on.

Important: you must un-do the previous infinite-loop logic from the section
above; returning the program to a normal loop. Also, remove the Refresh
command and other internal diagnostic MessageBox commands.

Steps:

1. Return the program to its original state by removing the diagnostics MessageBox
statement and the refresh line. Return the loop counter to normal.

2. Replace the textBox1 concatenation statement (line 8), replacing:


textBox1.Text = textBox1.Text + "hi ";

with this code:


textBox1.Text = textBox1.Text + Convert.ToString(loopCounter);

This is the statement that prints the numbers 1 through 10.

3. The final code should look like this:

Program 2.2 - while-loop with 1-10, complete with a formatting flaw


1 private void button1_Click(object sender, EventArgs e)
2 {
3 int loopCounter;
4 loopCounter=1;
5
6 while (loopCounter <= 10)
7 {
8 textBox1.Text = textBox1.Text +
Convert.ToString(loopCounter);
9
10 loopCounter = loopCounter + 1;
11 }
12 MessageBox.Show("Done");
13 }

5. Run the program by pressing F5, then clicking on button1.

Results: textBox1 displays "12345678910"

On line 8, append a space after the "Convert.ToString". The space is inserted at each
iteration loop and is part of textBox1 assembly.

Chapter 2 - Loops Page: 71


Comments on the 1-10 while-loop:

This program is no different than the "hi" version. The only minor complexity was
adding the loopCounter to the assembled text as it was moved into the textBox. Numeric
data (loopCounter) should be converted to a string before appending to a text field.

Question - Looping to 1000 - Why so slow?:

What happens if the while-loop changes from 10 to 100? What about 1000? If you have
a moderately old computer, a 2000-iteration loop takes a minute or so to calculate. At
increasingly larger numbers your program may appear to hang or it may appear to be in
an infinite loop. But if you wait long enough (and your upper limit is reasonable), the
program will complete the task.

Change your program to loop 1000 times now. Why so slow, knowing you probably
have a reasonably fast computer and 1000 iterations is nothing to a computer? The
appending statement:
textBox1.Text = textBox1.Text + Convert.ToText(loopCounter);

has to move a lot of text when it runs for a thousand times. All of the text, along with all
of the previous text, has to move in-and-out of the box with each iteration and each time
there is a larger amount of text to move.

And there is another problem. Since textBox1 is a visible field on the screen, C# spends
a lot of energy worrying about the user-interface, fonts, word-wrapping and the like.
There is a lot of behind-the-scenes work and it takes time.

The example can be slowed down even further by inserting "this.Refresh();" near the
bottom of the loop.

Speeding up the loop (in a contrived example):

Running the loop 1,000 times takes a surprising amount of time, even on a modern
computer. This non-sense program can be significantly improved by making a minor
change to the program's logic.

To do this, create a new string variable to house intermediate results. As the loop runs,
append all of the text to this intermediate variable. Since this variable is not part of a
display, the loop runs significantly faster. When the loop finishes, move the final (fully-
assembled) string to its final resting place in "one fell swoop." With this, the user-

Chapter 2 - Loops Page: 72


interface portion (font, word-wrapping, etc) runs only once, instead of 1,000 separate
times.

Make these changes to Program 2.2:

1. Near the top of the routine, at line 4a, declare a new string variable, "tempString" and
initialize it with an empty string, signified by a "" (quote-nospace-quote).

Replace line 8 with a new version:

1 private void button1_Click(object sender, EventArgs e)


2 {
3 int loopCounter;
4 loopCounter=1;
4a string tempString = "";
5
6 while (loopCounter <= 1000)
7 {
8 textBox1.Text = textBox1.Text + //Change these lines
Convert.ToString(loopCounter);
8a tempString = tempString + Convert.ToString(loopCounter) + " ";
9
10 loopCounter = loopCounter + 1;
11 }
12
12a textBox1.Text = tempString;
13 MessageBox.Show("Done");
14 }

The loop now manipulates tempString instead of the textBox. The structure and logic of
the statement remains the same

Notice variables do not have a ".Text" property; they are not "objects."
To compare:

textBox1.text = textBox1.Text + ....


tempString = tempString + ...

2. After the end of the loop's closing brace, at line 12/12a (see code above), move
tempString into textBox1.Text. This is key to the new design. Because of its position
outside of the loop, the move becomes a one-time event.

:
10 loopCounter++;
11 }
12a textBox1.Text = tempString;
13 MessageBox.Show ("done");
14 }

If you have played with the "this.Refresh();" command, remove it.

Chapter 2 - Loops Page: 73


Running the Faster Version:

The revised program reads like this.


Run the program (F5) and click button1 to execute the code.

Program 2.3 - 1000-iteration loop with speed, complete


1 private void button1_Click(object sender, EventArgs e)
2 {
3 int loopCounter;
4 loopCounter = 1;
4a string tempString = "";
5
6 while (loopCounter <= 1000)
7 {
8 tempString = tempString +
Convert.ToString(loopCounter) + " ";
9
10 loopCounter++;
11 }
12
12a textBox1.Text = tempString;
13 MessageBox.Show ("done");
14 }

Results: Lightning fast. 1,000 numbers, lickety-split.

Comments on the faster version:

The new string variable, tempString, was allocated above the loop, at line 4a, and was
initialized as an empty string. Initializing to an empty string is required because the
value must be "seeded" with data prior to the while-loop – even if initialized to "".

Without this, the compiler complains with "Use of unassigned local variable 'tempString'
when it first tries to work with the value in line 8. Previous 'textBox' versions of this
program did not have this problem because textBoxes automatically initialize with empty
strings when created.

It is a good idea to always initialize variables when they are


first declared because it can save headaches and problems
in later code. Pre-initialized variables get their memory
allocated immediately.

Initialize strings with "" (an empty-string); initialize


numbers, usually to zero.

Empty-strings "" are not null - they actually contain a value.

When progressively larger strings were appended to textBox.Text, the program labored
as larger and larger strings were moved in and out. The program had to worry about
wordwrapping and scrollbars. The improved version moves the data to a temporary
variable, tempString and does not concern itself with visual overhead.

Chapter 2 - Loops Page: 74


When the loop ends, textBox1 is still empty. Almost as an afterthought, at line 12a,
tempString is shoveled into textBox1, completing the program's mission.

Although this is a contrived example, it demonstrates a different way of accomplishing


the task. For normal textBox routines, you would never bother with a temp variable; the
performance gain isn't there. But in this example, a minor change in logic reduced the
runtime on my computer from 45 seconds to 1 second.

Multi-Lined textBox and Scrollbars:

You probably noticed the 1000+ loop's results did not fit in the allotted space within the
multi-lined textBox. It needs a scroll-bar.

After closing the program return to the Form Designer by using the top-row of tabs.
Highlight textBox1 (the field) and look at the Properties panel on the lower-right. You
may find from your previous testing the Output tab overlays the Properties tab. The
Output tab can be closed:

In textBox1's Properties pane, find the setting for "Scrollbars." (It is helpful to click the
A-Z sorting button at the top of the pane.) In other words, "Scrollbars" is a property of
text. Select "Vertical".

Chapter 2 - Loops Page: 75


Run the program and confirm the scrollbar is present; all values should display.

Chapter 2 - Loops Page: 76


"do" Loops

Compared to while-loops, "do" loops are anti-climatic. These types of loops are
identical to "while" loops in every respect except one: the conditional is checked at the
end of the loop and this is reflected in the code.

do
{
<code inside of loop>

} while (condition-test);

Why bother? The difference is subtle, but sometimes useful. A do-loop always runs at
least once, then it checks the conditional. Contrast this with a while-loop – it checks the
conditional before attempting the first iteration.

do-loops:
• Always runs at least one time
• Checks the condition at the end of the loop - always running at least once
• loopCounter must be incremented

while-loops:
• May or may not run, depending on the condition
• Checks at the start of the loop, can bail early
• Like a while-loop, the loopCounter must be incremented

do-loops are otherwise identical to a while-loop but the syntax is somewhat unusual and
there is a seemingly oddly-placed semicolon, sitting at the end of the while-clause.
Meanwhile, like all other loop statements, there is no semicolon on the "do" keyword.

In my experience, these types of loops are used infrequently. Since this loop is similar to
the while-loop.

Program 2.3b - 1000-iteration do-loop with speed, complete


1 private void button1_Click(object sender, EventArgs e)
2 {
3 int loopCounter;
4 loopCounter = 1;
5
6 do
7 {
8 textBox1.Text = textBox1.Text +
Convert.ToString(loopCounter) + " ";
9
10 loopCounter++;
1 } while loopCounter <= 1000;
2
3 MessageBox.Show ("done");
4 }

Chapter 2 - Loops Page: 77


for-next Loops

"while" loops are useful because they can run when the total number of loops are
unknown, running until a condition is true; the condition (the count) is usually not
known at the start of the loop. For the illustrations, the while-loops were limited to 10
(or 1000) iterations, but in real life, they would use a variable or perhaps another user-
event to control how many times the loop ran. Commonly, while-loops process input
files while "not End-of-File" (and the end-of-the-file is not exactly known), or "while
string_Input is equal to Smith", etc.

But there are times when you know exactly how many times to run the loop. For
example, you may need to skip across each month in a year, or each character in a string,
examining one character at a time. In both of these examples the number of iterations
are known (12 months in a year, length of the string). This type of loop is called a "for"
loop, but I call them by an older name, "for-next loops." Although you can make a
while-loop do the same thing as a for-next, there are reasons to use this new species of a
loop.

"for-next" loops have these characteristics:

• Total number of iterations must be known (unlike most while-loops).


• All loop-control variables are defined on one line.
• No need to "manage" the loop counters within the loop; self-counting.
• Loop condition is checked before the loop runs; the content may or may not run,
depending on the comparison. Similar to a while-loop.
• Concise, tight syntax.

Probably the best reason to use for-next loops is it keeps track of its own counters and
variables – all within one line of code. Since the controls are in one place, the loops are
often easier to read and interpret than other types of loops.

Even though C# doesn't use the keyword "next," I still call the loop a
"for-next" out of habit, from other programming languages. Basic
used the word "next" to close the loop. Calling the loop a for-next is
easier and more descriptive to read in a book because the word
"for" by itself is odd.

Basic for-next loops:

Using the same program from other examples in this chapter, follow these steps, with
new logic attached to button1. The program will print the numbers one through ten,
displaying the results in the multi-lined textbox, textBox1.

1. From either the form's design view, double-clicking button1, or from code view, locate
the button1_Click event and remove all statements between the event's opening and
closing braces, leaving an empty event:

Chapter 2 - Loops Page: 79


private void button1_Click (object sender, EventArgs e)
{

2. In the button1_Click event, write these statements. Line 5 is the heart of the loop and the
details are described in a moment:

Program 2.4 - Print the numbers 1-10 with for-next, complete


1 private void button1_Click(object sender, EventArgs e)
2 {
3 int loopCounter = 0;
4
5 for (loopCounter = 1; loopCounter <= 10; loopCounter++)
6 {
7 textBox1.Text = textBox1.Text +
Convert.ToString(loopCounter) + " ";
8 }
9
10 MessageBox.Show ("done");
11 }

3. Run the program with an F5.


Press button1

Results: textBox1 contains: "1 2 3 4 ... 10", where each loopCounter is converted to a
text-string and then appended to textBox1.

for-next Structure and Design:

for-next loops are composed of three separate phrases and each phrase handles either the
beginning, middle or end of the loop.

Chapter 2 - Loops Page: 80


where:

• The first phrase is the starting value of the loop - initializing the loop's first value, in
this case, "1". After this, the phrase is ignored for the remainder of the loop.

• The second phrase is the conditional - run the loop while the counter's condition is
true. This condition is checked *before* each iteration.

• The third phrase, at the end of the statement, increments the loop's counter.
Although this is the third phrase in the loop, this part of the statement takes effect at
the bottom of the loop, just before the condition is checked.

• For the increment, you can use either of these three styles and the differences are
immaterial:

loopCounter++ (recommended)
++loopCounter
loopCounter = loopCounter + 1

• Each phrase is separated from the other with an internal semicolon. And, like a
"while" loop, there is not a closing semi-colon at the end of the statement.

Declaring and Initializing at the Same Time:

The variable that controls the loop must be declared and initialized before the loop
begins. On line 3, loopCounter is declared as an integer, "int loopCounter;" and out of
habit I typically, and optionally, initialize the variable with a value, "int loopCounter =
0".

At line 5 phrase-1, the value is (re-) initialized to 1, as in:

for (loopCounter = 1; loopCounter <= 10; loopCounter++)

The variable's declaration and initialization can be combined into one by declaring the
integer and assigning an initial value with the first clause of the for-next loop. Because
this integer is only used for the loop, most programmers prefer this design:

The resulting statement at line 5:

for (int loopCounter = 1; loopCounter <= 10; loopCounter++)

Chapter 2 - Loops Page: 81


If you declare within the loop, you must remove the statement at line 3 or
the two declarations will collide with a compiler error.

loopCounter as a Throw-Away Variable:

Because loopCounter exclusively controls the for-next loop, it is disposable. Usually the
variable isn't needed anywhere else, and for that reason it does not even warrant a formal
declaration or even a legitimate name. Being so trivial, many programmers simply use
the letter "i" for the variable name (this goes back to the Fortran Programming language
in the 1960's).

For the sake of these illustrations, a descriptive name "loopControl" is being used (or as
you will see in future chapters, "iloopCounter" where the "i" means "integer"), but even
the author often uses a variable simply named "i". It is a sad commentary on the laziness
of programmers. The loop is most-often written like this:

for (int i = 1; i <= 10; i++)

How to Read a for-next Loop:

Reading a for-next loops takes practice. Here are some English hints.

In the old days (QuickBasic, VB), this loop was typically read as "for eye equals one to
10, step one" but that didn't really explain the loop to the un-initiated. It would be more
precise to say it like this: "Start eye at one and count forward by one. Do this 'while' eye
is less than or equal to 10. If eye climbs above 10, stop the loop." This type of verbiage
emphasizes the fact that a "while" loop and a "for-next" loop essentially operate in the
same way because both use "while" in their definition.

Incrementing a for-next loop:

The details inside the loop's opening and closing braces are nearly the same as the while-
loop examples – with one important exception: there is not a loop-incrementing
statement in the loop's details.

Chapter 2 - Loops Page: 82


In a "while" loop, a incrementing statement was absolutely required (if not, think infinite
loop). But for-next loops handle this automatically with the third-phrase of the
statement. The increment lives in the loop's definition - not in the loop's details.

How a for-next loop works:

When the loop is first encountered, C# initializes the loopCounter variable as shown in
the first part of the phrase. Next, it checks the conditional to make sure it has the
authority to run the loop – is the loopCounter less than 10? If the condition allows, the
code within the opening and closing brace runs.

For most for-next loops, the count almost always starts with
"1" and loops until some other value. But values other than
one could be variablized, meaning the loop could start at
"9" or "100". The for-next loop still checks for permission
before running.

When each iteration, the loop reaches the closing brace at line 8; the counter increments
by one; using the third phrase and then it loops back to the top and checks the condition.
Consider the closing brace as the "next" part of the loop. Ultimately loopCounter
reaches the cut-off point and the loop ends with the next statement after the closing brace
as the next command.

Semi-Colon Errors:

Out of habit, it is easy to add an errant semicolon at the end of the for-next statement, as
in:

The compiler will complain: "Possibly mistaken empty statement." Delete the semi-
colon.

Using a Carriage-Return/LineFeed (CRLF):

The examples in this chapter have printed their results in a horizontal list. With a minor
change, the numbers can display vertically by introducing a carriage-return/linefeed:

Chapter 2 - Loops Page: 83


The previous examples appended a space (e.g. " ") as each new value was written:
textBox1.Text = textBox1.Text + Convert.ToString(loopCounter) + " ";

Change the space to CarriageReturn/LineFeed with these characters:


+ "\r\n"

where the string "backslash-r, backslash-n" represents two ASCII character codes,
CHR$(10) + CHR$(13). (ASCII being a "character set" which includes the letters A-Z,
a-z, numbers, tabs and other special characters).

In C#, a \backslash indicates a "special character" (reserved, usually for non-viewable


characters). Thus, a "\r" is a "return - or carriage return" and "\n" is a new-line
(linefeed). "\r\n" is often called CRLF. This harkens back to manual typewriters and
mechanical line printers.

The completed code:


Program 2.45 -Print the numbers 1-10 vertically, complete
1 private void button1_Click(object sender, EventArgs e)
2 {
3 int loopCounter;
4
5 for (loopCounter = 1; loopCounter <= 10; loopCounter++)
6 {
7 textBox1.Text = textBox1.Text +
Convert.ToString(loopCounter) + "\r\n";
8 }
9
10 MessageBox.Show ("done");
11 }

There is more about special characters in future chapters.

Chapter 2 - Loops Page: 84


Vertical Scrollbars:

As before, the resulting list may not fit in the space allotted in textBox1. If the textBox
did not have a vertical scroll bar, users can still place the cursor in the box and arrow
down to reveal the remaining numbers - but without a scrollbar, users have no indication
this is possible. If you have not already done so, close the program and return to design
view and set textBox1's ScrollBar property to "Vertical."

Exercise: Use a for-next Loop to Add the Numbers 1 to 100:

Write a for-next loop that adds all the numbers between 1 and 100. Attach the logic to
button1. Display the results in a MessageBox or in textBox1. The answer is calculated
as: 1 + 2 + 3 + 4...+ 100 = 5050. A for-next loop is being used because the count is
known.

A new, intermediate variable is needed to hold the results of the calculation. Although
any variable name will do, one named grandTotal seems descriptive. Attempt to write
the code now, but as a hint, the loop's logic will work like this:

The loop starts out with a grandTotal of zero.

- On iteration 1, the grandTotal is the previous total, zero, plus 1

- At iteration 2, the grandTotal is the previous grandTotal plus the value of the
loopCounter (2), giving a total = 3

- On loop number 3, the new total is the previous grandTotal, 3 plus the value of the
loopCounter, 3; 3 + loopCounter=3 = 6.

Before reading further, attempt writing the routine now. You may be surprised to learn
the interior of the loop is one line long. Here is the solution:

Chapter 2 - Loops Page: 85


Exercise: Adding the Numbers 1 thru 100 using a for-next loop, complete
1 private void button1_Click (object sender, EventArgs e)
2 {
3 int grandTotal = 0;
4
5 for (int loopCounter = 1; loopCounter <= 100; loopCounter++)
6 {
7 grandTotal = grandTotal + loopCounter;
8 }
9
10 MessageBox.Show
("The Total value is: " + Convert.ToString(grandTotal));
1 }

where:

• Above the loop, a variable "grandTotal" is declared and initialized to zero.


Initializing the grandTotal to zero is important because a value must pre-exist before
it can be used on the right-side of the equal-sign in statement 7.

• The integer loopCounter is declared and initialized as a throw-away variable within


the loop's definition, line 5. It exists and is only needed for the duration of the loop.

• With each iteration, the current loopCounter's value is added to grandTotal and the
results are put back into grandTotal. This is the same type of formula as i = i + 1.
The only difference is a loopCounter-value is being added instead of a simple
number 1.

• The MessageBox shows the results *after* the end of the loop because it is after the
loop's closing brace. No details are being written in TextBox1.

Exercise: What is the result if looped 1,000 times? 10,000 times? This is
mathematically interesting and you are encouraged to try. There is a simpler
mathematical formula to calculate this, but with the computer, we can use brute force.

Exercise: Write this same loop using a while-statement.

Chapter 2 - Loops Page: 86


Variations on "for-next" Loops

for-next loops are interesting because they can have different starting values. You can
start the loop below zero or it can start at an intermediate number. The loop can grow in
increments other than one and it can run backwards, counting from high numbers to low.

Granted, "while" and "do" loops can also do this, but a for-next loop does this with
particular grace and it is self-documenting, looking at one line of code. Below are some
examples on the different ways to start a for-next loop.

Starting at a number other than 1:

There is no law that says you have to start a for-next loop at 1. They can start at zero, a
negative number, or any other number. "Arrays" often need to start at positions other
than one.

Consider this version of a for-next loop: It starts at 5 and results in "5 6 7 8 9 10":

for (int iloopCounter = 5; iLoopCounter <= 10; iloopCounter++)


{
textBox1.Text = textBox1.Text +
Convert.ToString(iloopCounter) + "\r\n";
}

Incrementing by a number other than 1:

You also don't have to increment by one. Consider this snippet which results in this
string: "2 4 6 8 10" and uses a variable "i":

for (int i = 2; i <= 10; i = i + 2)


{
textBox1.Text = textBox1.Text +
Convert.ToString(i) + "\r\n";
}

where:

• the increment is by twos. Because of this you must use the older-styled increment
"i = i + 2" instead of "i++".

Exercise: Loop from 1 to 50, stepping by 5.

Looping Backwards from 10 to 1:

for-next loops can start at a higher number and run backwards. When this is done, the
conditional flips from a normal "while less-than or equal-to" to "greater-than or equal-to"

Chapter 2 - Loops Page: 87


and the increment becomes a decrement. The results are a count-down from
"5, 4, 3, 2, 1, 0". This type of loop is often used by NASA.

for (int i = 5; i >= 0; i = i - 1)


{
MessageBox.Show ("Tee-minus " + Convert.ToString(i));
}

MessageBox.Show ("Blastoff!");

The decrement, "i = i - 1", could have been written as:

i--; (eye minus-minus)

If your code ran – but displayed a single Tee-minus and then jumped immediately to
blastoff, then the conditional was set incorrectly; it was probably set to "while i >= 5".

//Incorrect countdown timer


for (int i = 5; i >= 5, i--)
{
:

With this improperly-set conditional, i starts at 5 and since "5" is greater-than-or-equal-to


"5", it does not meet the condition and the loop stops prematurely, having not run a
single iteration.

Study the correct line for a moment. "Start eye at 5 and count backwards by 1. Do this
'while' eye is greater than or equal to zero. When eye falls below zero, stop the loop."

for (int i = 5; i >= 0; i--)


{

The other common way to incorrectly write this loop is to write the conditional as
"while i <= 0" where the greater-than was written as a less-than. This results in an
instant "blastoff," with no countdown, and it always surprises the launch crew.

Starting too High:

There is this possibility: What if your starting value is greater than the conditional? In
other words, what if "i" is initialized at 5,000; in this case none of the code within the
loop runs.

for (i=5000; i <= 10; i++)


{
textBox1.Text = textBox1.Text +
Convert.ToString(i) + "\r\n";
}

Results: The loop "runs," but does not execute because the loop-counter failed the "less
than or equal to 10". Naturally, in the real world, the 5,000 would not be hard-coded in
the loop. Instead, it would be a variable set by another routine.

Chapter 2 - Loops Page: 88


Controlling Loops with Variable textBoxes

In previous examples the loops were controlled with hard-coded values (i <= 10, etc.).
Now, using skills from Chapter 1's textBoxes, use a typed value to change the loop's
iterations based on a variable. For these next examples, use textBox2 to control the for-
next loop. For example, type "15" in textBox2 and loop 15 times.

Converting Strings to Integers:

By definition, textBoxes are strings and their values cannot be used in numeric
calculations or comparisons until converted to a number. You have seen the opposite,
where numbers are converted to strings using: "Convert.ToString(loopCounter)"

There is a converse command called "Convert.ToInt32".

Exercise:

Modify the example program, exchanging the hard-coded <= 10 with a converted
textBox2.Text. Detailed steps are documented next.

Controlling a Loop with a textBox Entry:

1. Using Program 2.2, and others in this chapter as a model (counting 1 to 10), modify
button1's logic so it uses textBox2 to control the duration of the for-next loop. Write the
results with CRLF's after each printed number. Here is the final code:

Chapter 2 - Loops Page: 89


Program 2.6 - Controlling loops With textBox Controls, initial
1 private void button1_Click(object sender, EventArgs e)
2 {
3 int loopCounter;
4
5 for (loopCounter = 1;
loopCounter <= Convert.ToInt32(textBox2.Text);
loopCounter++)
6 {
7 textBox1.Text = textBox1.Text +
Convert.ToString(loopCounter) + "\r\n";
8 }
9
10 MessageBox.Show ("done");
11 }

When testing, type only numeric values in textBox2; audit logic has not been written and
the program will crash if otherwise. If you find you cannot type a value into textBox2,
check the "Enabled" property, confirming it is set to "True" (set in an example from
Chapter 1).

where:

• The for-next loop's conditional uses a new term at line 5: "Convert.ToInt32". This
converts textBox2 from a dot-text string to a 32-bit integer – making the string
suitable for numeric calculations. An audit should be written here, intercepting
invalid numbers. This is covered in later chapters.

• Because of the length of the new statement, the for-next loop was written on three
separate lines, making for easy-to-read code. This is considered good programming
style.

Chapter 2 - Loops Page: 90


Possible Error - The infamous forgotten ".Text":

As you keyed the phrase "Convert.ToInt32(textBox2.Text);", what would happen if


you forgot to type the 'dot-Text' and then tried to run the program?

The program would crash with a runtime error, generating this difficult error message:
"System.InvalidCastException: Unable to cast object of type
'System.Windows.Forms.TextBox' to type 'System.IConvertible'". Press the tool-bar's
red-square to end the program and return to the editor.

You will see a similar message if textBox2 is blank: "System.FormatException: 'Input


string was not in the correct format.'

Exercises: Running the Program with a variety of inputs:

Run the program and answer the following questions.

1. Run the program (F5)


Type a reasonable numeric value in textBox2
Click button1.
(Results: The program runs as expected.)

2. Try a negative number (e.g. -15) in textBox2 and click button1.

(Results: The program runs but no new results are displayed (previous run results
remain unchanged. Note that the previous results were not erased; this will be fixed in a
moment.)

Question:
Why doesn't the loop run? (It is important that you understand the reason for this. The
answer: Looking at the 'while' clause – "is 1 less-than-or-equal-to negative 15?" No, the
first iteration, 1, is greater than the negative number – the loop does not have permission
to run.)

Chapter 2 - Loops Page: 91


3. Type a non-numeric value, such as "dog" in textBox2 or leave textBox2 blank.
(Results: A crash. Routines to intercept this are covered in the future. For now, note
how the compiler helps or hinders in debugging the problem. Click the ribbon's red-
square to return.)

Exercise: Clearing TextBox1 with Each Run:

Modify the code so each time button1 is pressed, textBox1 starts with an empty box and
the previous results are erased. Try this now. Hint: Can you move anything to clear or
erase textBox1.Text prior to the loop?

Solution: At line 3a, add this statement, where quote-quote represents a null string:
textBox1.Text = "";

or better yet, use this method, specifically designed for the task:

textBox1.Clear();

or use the performance-enhancing technique of using a temporary string variable instead


of writing directly to the text box (see line 3a and 7).

Using an intermediate string variable to clear the previous run, complete


1 private void button1_Click(object sender, EventArgs e)
2 {
3 int loopCounter;
3a string tempString = "";
4
5 for (loopCounter = 1;
loopCounter <= Convert.ToInt32(textBox2.Text);
loopCounter++)
6 {
7 tempString = tempString +
Convert.ToString(loopCounter) + "\r\n";
8 }
9
9a //Notice how this clears the previous test-runs and
9b //acts as a performance-enhancement
9c textBox1.Text = tempString;
10 MessageBox.Show ("done");
11 }

Why does this technique work without using a .Clear( )?


Answer: line 9c completely replaces textBox1.Text.

Chapter 2 - Loops Page: 92


Interrupting Loops

Sometimes, while running a loop, you may need to skip that iteration's processing and
jump to the next item. For example, while looping through payroll records, you may
discover a record or condition that shouldn't be processed due to a data-entry error or
some other flag. Perhaps certain account-codes should be ignored or transactions prior
to a certain date are bypassed. The need for this happens often enough that C# provides
two loop controls just for this purpose:

continue; The current loop iteration immediately ends and starts with the next loop
cycle. When "continue;" is reached, all remaining lines in the loop's details
are skipped and control is passed to the top of the loop. Usually, a continue-
statement is used when a record has some kind of error or exception, but the
error is not serious enough to stop the program. Use with care in a while-
loop.

break; Causes the entire loop to immediately end and control is passed to the next
statement after the loop's closing brace. This does not end the program or
the current routine, only the loop. Usually break statements are used when a
catastrophic data-error is discovered and all processing of a (file) should be
immediately stopped.

Use these statements in any type of loop, including "while", for-next, and do-loops - but
in my experience I most often see them in for-next loops.

'continue' works especially well in a for-next loop but use with great care in
a while-loop. Details later in this section.

Decisions like these always use an "if-statement" somewhere in the interior of the loop to
make a decision. Although "if-statements" are covered in the next chapter, you should
be able to follow these examples.

"continue" Example:

This example uses program 2.4 as a base. The goal is to loop 1 through 20, but skip
items 7 and 13. And just to demonstrate the break command, stop all processing at item
15. This is a contrived example.

Program 2.4, repeated here


private void button1_Click(object sender, EventArgs e)
{
for (int iloopCounter = 1; iloopCounter <= 20; iloopCounter++)
{
textBox1.Text = textBox1.Text +
Convert.ToString(iloopCounter) + "\r\n";
}
}

Chapter 2 - Loops Page: 93


Continue statements are almost always used near an if-statement. The clause needs to be
placed high enough in the loop so it skips the work being done. The "if" syntax is:

if (iloopCounter == 7)
continue;

The next chapter discusses if-statements in more detail but here are the important points:

• if-statements use double-equals (= =) for the comparison.


• The condition you are checking is enclosed in parenthesis
• Like many of the loop commands, it does not end with a closing semicolon.

Program 2.7 - Continue-Statements


private void button1_Click(object sender, EventArgs e)
{
for (int iloopCounter = 1; iloopCounter <= 20; iloopCounter++)
{
if (iloopCounter == 7)
continue;

if (iloopCounter == 13)
continue;

textBox1.Text = textBox1.Text +
Convert.ToString(iloopCounter) + "\r\n";
}
}

In this example, I prefaced the variable "loopCounter" with the


letter "i" (iloopCounter), acting as a visual reminder this is an
integer. The prefix "i" for integer, "str" for string, "lbl" for
label, etc., is called Hungarian Notation and I will be using this
notation in all of the later examples to help you understand the
variable's "type." Understand that Microsoft, and many shops,
do not condone this type of variable naming.

Test the "continue" Statements:

1. Press F5 to run the new program.


Click on button1.

Confirm that the output shows all numbers, 1 - 20, skipping 7 and 13.

Chapter 2 - Loops Page: 94


break Statements:

Now add the "break" statement that ends the loop pre-maturely at 15. (The loop should
have been written "while iloopCounter < 15" instead of using obtuse logic like this
example.)

Program 2.71 - break-Statements


private void button1_Click(object sender, EventArgs e)
{
for (int iloopCounter = 1; iloopCounter <= 20; iloopCounter++)
{
if (iloopCounter == 7)
continue;

if (iloopCounter == 13)
continue;

if (iloopCounter == 15)
break; //Numbers 16-20 never run

textBox1.Text = textBox1.Text +
Convert.ToString(iloopCounter) + "\r\n";
}
}

Test the "break" Statements:

Test the program by pressing F5 and clicking button1.


Confirm 7 and 13 are still missing
Confirm 14 is the last number printed.

Chapter 2 - Loops Page: 95


comments:

• Having two if-statements, with two separate 'continue' clauses is inefficient and
wordy. The two phrases could be combined into one statement using a double-"OR"
clause. This is described in more detail in the next chapter.

if (iloopCounter == 7 || iloopCounter == 13)


continue;

"while-loops and 'continue':

Using a 'continue' in a while-loop requires more thought. If a 'continue' is called, control


jumps to the end of the loop, directly to the closing brace, bypassing all intermediate
code – including the loopCounter increment. This can put a program into an infinite
loop, especially if the if-statement is using the loopCounter. Consider this code-snippet:

:
int iloopCounter = 0;

while (iloopCounter <= 10)


{
if (iloopCounter == 7)
continue;

textBox1.Text = textBox1.Text +
Convert.ToString(iloopCounter) + "\r\n";

iloopCounter++;
}
:

The 'continue' will jump to the while-statement's closing brace, bypassing the
loopCounter. Because of this, iloopCounter will never grow past 7 and will never reach
the upper-limit, 10.

Real-world examples of this problem will be seen in the string-parsing chapters. To


resolve this issue, more knowledge about if-statements is needed. In summary, the
solution is this:

:
if (iloopCounter == 7)
{
iloopCounter++; //A duplicate increment is needed
continue;
}
:

Variable Scope Concerns:

In a for-next loop, integer declarations can happen above the loop with a stand-alone
"int iloopCounter = 1;" or the variable can be declared and initialized within the for-
next statement using "for (int iloopCounter = 0; ...)".

Chapter 2 - Loops Page: 96


Declaring the variable within the for-next loop has the benefit of being compact and
quick and it has another benefit: Once the loop ends, the variable is discarded, freeing
memory. But what happens if you wanted to use the variable's value later in the
program?

Consider this example, which declares the variable "iloopCounter" above the loop and
then displays the final loop-counter value after the loop completes; note how
"int iloopCounter" is declared above the loop:

Program 2.75 - Variable declared outside of scope


private void button1_Click(object sender, EventArgs e)
{
int iloopCounter;

for (iloopCounter = 1; iloopCounter <= 10; iloopCounter++)


{
textBox1.Text = textBox1.Text +
Convert.ToString(iloopCounter) + "\r\n";
}
MessageBox.Show("The final value of 'i' is: " +
Convert.ToString(iloopCounter);
}

Now change where integer iloopCounter is declared, moving it into the loop definition:

Program 2.76 - Flawed variable out of scope


private void button1_Click(object sender, EventArgs e)
{
int iloopCounter;

for (int iloopCounter = 1; iloopCounter <= 10; iloopCounter)


{
textBox1.Text = textBox1.Text +
Convert.ToString(iloopCounter) + "\r\n";
}
MessageBox.Show("The final value of 'i' is: " +
Convert.ToString(iloopCounter);
}

When running this version, you will get a compiler error, flagging the MessageBox
statement with, "The name 'iloopCounter' does not exist in the current context."

In this case, the integer was declared within the for-next and died when the loop ended.
Any code referencing the loopCounter fails because it is un-declared and un-available.
Similarly, no matter where the variable is declared within button1's Click event, it is
unavailable to any other button or routine elsewhere in the program. Once button1
reaches it's closing brace, all references to variables within are destroyed. In other
words, the "scope" of the variable is limited to the routine that declared it. This topic
will be explored in more detail when Functions and Methods are described.

Chapter 2 - Loops Page: 97


Nested Loops

A loop within a loop is called a "nested loop." This topic may seem esoteric, but nested
loops are often used in array work and when processing files with sub-files or other 2
and 3-dimensional data sources. As scary as they sound (and look), nested loops are
interesting to work with and can do things that no other construct can.

In this section write a routine that generates a simple multiplication table and second
routine that displays an ASCII-character Christmas tree. These examples demonstrate
nested loops and they are purposely simplified in order to explain the concepts.

Building the Example Program:

Construct the example program by taking the same routines you've been working with in
the for-next examples (Program 2.2 or 2.4) and making the following cosmetic changes:

1. "Stretch" Form1 to make it a little taller.


2. "Stretch" textBox1, making it a bit taller; in order to do this, the Multi-line property must
be equal to true.
3. Select textBox1 and set the "Font" Property to "Courier New"

The form should look similar to this illustration:

Multiplication Table with Nested Loops:

The goal of the first nested-loop example is to generate a short 7x5 multiplication table
where

Chapter 2 - Loops Page: 98


row 1 = 1,2,3,4,5
row 2 = 2,4,6,8,10
row 3 = 3,6,9,12,15
etc.

Although not pretty, the on-screen results will look like this:

Consider how this routine is likely built. A normal multiplication table takes a row-
number times a column number. Thus,

row-2, column 3 equals 2 x 3 = 6


row-5, column 4 equals 5 x 4 = 20

This can be exploited with two loops, one dependent on the other. The first loop
controls the number of rows (7) and the second controls the columns (5). The column-
loop will be inside of the row-loop.

Building the Table:

Program 2.8: Multiplication Tables

1. Begin by deleting all previous code in button1's Click event, leaving the event's opening
and closing braces.

2. Since the number of rows and columns are known (7x5), for-next loops are the best
candidates to use with the loops.

Begin with the "row" loop within button1's Click event:

private void button1_Click (object sender, EventArgs e)


{
for (int row = 1; row <= 7; row++)
{

}
}

Chapter 2 - Loops Page: 99


The for-next loop has its own set of braces and the indenting keeps them visually
separate from the button's braces:

where:

• The for-next loop declares and initializes the "row" variable at 1


• The loop will count from 1 to 7
• Increment using row++

Consider the inner Loop:

Each row has five columns. Imagine row #1: while on the row, skip from column 1, to 2,
to 3.... Then, when you are done with the five columns, move to the next row (#2) and
start over with a new column-count, 1, 2, 3....

3. This is accomplished by placing a column loop within a row loop:

private void button1_Click (object sender, EventArgs e)


{
for (int row = 1; row <= 7; row++)
{
for (int col = 1; col <= 5; col++)
{

}
}
}

j For each outside row-loop, the columns run from 1 to 5. This means the interior
column loop is busier than the outside loop. Notice how the column variable, "col",
is re-created / re-initialized to 1 each time the row-loop runs.

Chapter 2 - Loops Page: 100


4. Inside the inner-most (col) loop, insert a statement that calculates the current row-
number times the current column number, printing the result. Be sure the statement fits
between the two nested braces; it will be indented the furthest:

:
for (int row=1; row <= 7; row++)
{
for (int col = 1; col <= 5; col++)
{
textBox1.Text = textBox1.Text +
Convert.ToString (row * col) + " ";
{
:

Notice the appended space (" "), which separates one digit from the next:
1 2 3 4 5

This is testable now, with a flawed result.

5. This leaves one problem: If the program were to run now, all the results would print as
one horizontal line, where each row appends to the previous:

1 2 3 4 5 2 4 6 8 10 3 6 9 12 15 ... etc.

A CarriageReturn/LineFeed is needed after the column-loop, giving

1 2 3 4 5
2 4 6 8 10
3 6 9 12 15 ...

This is a trick but is typical with nested loops. After the column loop completes its
count, 1-5, and before looping back up for the next row, append a CRLF to textBox1 at
the end of the column loop (outside the col-loop, but before the next Row loop):

Chapter 2 - Loops Page: 101


The completed multiplication table is shown here.

Program 2.8 - Multiplication Table, complete


private void button1_Click (object sender, EventArgs e)
{
textBox1.Text = ""; //clear previous run

for (int row = 1; row <= 7; ++row)


{
for (int col = 1; col <= 5; ++col)
{
textBox1.Text = textBox1.Text +
Convert.ToString(row * col) + " ";
}

//Insert a linefeed after each row


textBox1.Text = textBox1.Text + "\r\n";
}

Testing the Multiplication Table:

Press F5 to run the program; then click button1.


Results: A 7x5 multiplication table appears in textBox1.

Chapter 2 - Loops Page: 102


where:

• A loop-within-a-loop gives a graceful way to calculate each cell in the multiplication


table. The entire routine consists of two loops and two statements.

This logic is more useful than in multiplication tables. Arrays and other table work
often uses this type of logic.

• When a row's columns are completed, insert a CarriageReturn/LineFeed (\r\n). In


other types of record processing, this same location usually calculates subtotals or
preps for the next transaction.

• In order to display better, the textBox was set to a non-proportional font (Courier
New). This helps line up the columns. But the first and second rows clearly do not
line up because they are one digit wide. Fixing the spacing issues can't be resolved
with the tools discussed so far, but in summary, any of these methods would work:

a) Use an if-statement on the numeric-size, padding extra spaces


b) Use formatted printing techniques.

These methods will be discussed in future chapters.

Exercise: Change the table to a 10x10 array.

Chapter 2 - Loops Page: 103


Exercise: Nested Loops: A Christmas Tree

As an optional exercise, use a nested loop to print a triangular-shaped Christmas tree.


Granted, this is not a particularly useful but it further explains the nature of a nested
loop. If you understand this example, you'll have a solid grasp on nested loops.

The goal is to build a triangular shape using pound-signs (hash marks). The "tree" has 7
rows with a varying number of columns. The end result will look like this:

Building the Tree:

1. As before, modify button1's Click event by removing previous logic. Be sure to leave
the event's opening and closing braces. textBox1 remains a multi-lined textbox,
formatted with a non-proportional font.

Ultimately, there will be three loops:


The "row" loop controls the number of rows for the tree – rows 1-7.
A second, nested column loop controls how wide that part of the tree is.
The third and final loop is unrelated and it builds the tree-trunk.

2. Following the multiplication table example, add an outside "row" loop with a nested,
inside "col" loop.

On row 1, there is 1 column


On row 2, there are 2 columns
On row 3, there are 3 columns

Chapter 2 - Loops Page: 104


In the multiplication table, the column-loop was set to 5-wide. With this new
requirement, tie the column to the row number. If on row-2 then limit the columns to 2
columns. This is accomplished by replacing the (previously-hard-coded "5" (7x5)) with
the row-variable:

private void button1_Click (object sender, EventArgs e)


{
for (int row = 1; row <= 7; row++)
{
for (int col = 1; col <= row; col++)
{

}
}
}

3. Except for the tree-trunk logic, the rest of the loop remains the same, printing hash-marks
"#" instead of a calculation. As before, at the end of a column loop, print a CRLF.

Use either the traditional appending statement or the newer method:


textBox1.Text = textBox1.Text + "#";
textBox1.Text += "#";

Here is the code for the completed program. Note little changed from the previous
multiplication table example:

Program 2.85 - Nested Christmas Tree, nearly complete


private void button1_Click(object sender, EventArgs e)
{
for (int row = 1; row <= 7; row++)
{
for (int col = 1; col <= row; col++)
{
textBox1.Text = textBox1.Text + "#";
}

textBox1.Text = textBox1.Text + "\r\n"; //line feed


}

// * Trunk logic goes here

Chapter 2 - Loops Page: 105


The loop, explained:

In this code, notice how the column for-next loop is within the row-loop. This means for
each row (1,2,3), the column loop runs multiple times. Without actually running the
program, study the code for a minute and mentally keep track of what happens to the
column variables when the row-count is equal to 1. Then, mentally bump the row-count
to 2 and study the column loop a second time.

The interior column loop is tied to the row-count. When the row is 1, the column count
runs from one-to-one. When the row is 2, the column count runs from 1 to 2. When the
row is 7, the column count runs from 1 to 7.

Additional Comments on the Tree loop:

This is admittedly a strange concept. Here is the logic of the loop; look at the code and
follow along. The code is testable now:

• The row loop cycles from 1 to 7. Note the 7-tree-rows.

• As each row is reached (1, 2, 3...), the col loop runs, cycling through each column
before moving to the next row. This is the key to the program. The number of
columns varies with the row-count.

• At the end of each column-loop, after that row's #'s are printed, append a
CarriageReturn\LineFeed "\r\n". The CRLF's are added at these locations:

Chapter 2 - Loops Page: 106


The Tree-Trunk:

In the code above, after the loop, look for a comment: "\\ Trunk logic goes here".
Add this logic at that location:

Tree-trunk logic, partial code, complete


:
for (int row = 1; row <= 2; row++)
{
textBox1.Text = textBox1.Text + "@@\r\n";
}

This is a normal, non-nested loop that prints the tree trunk. The loop literally prints
"@@" twice, with a CRLF after each print. The "\r\n" can be inserted into an existing
string or appended as a separate string.

The same row-variable is being used because it was no-longer needed in the tree's
construction. In other words, the tree-trunk does not have to begin with row 8 because
this part of the program is unrelated (logically) to the other. If you wanted to start at 8,
then you would have to modify the program to read like this:

for (int row = 8; row <= 10; row++)


{
textBox1.Text = textBox1.Text + "@@\r\n";
}

Because the original nested-loop's row-variable was destroyed when the main tree-loop
ended, this new loop needs to re-declare the "int row" variable. As an aside, you could
have declared a different variable name, such as "trunkCount", making it self-
documenting.

Chapter 2 - Loops Page: 107


foreach Loops

"foreach" loops are specialized loops that work only with arrays and they are
superficially introduced here because of the chapter's topic. Arrays and foreach loops
are covered in more detail in Chapter 22 (Arrays) and in Chapter 13 (Parsing).

If you are new to programming, arrays will be a foreign concept and you can safely skip
this section, but in many respects, these are the simplest of all loops because they handle
all aspects of the loop automatically.

Arrays are a variable that can hold one or more items in a list. You can think of them as
a mini-spreadsheet. For example, an array of names might appear like this, where the
array is named "alistOfNames":

Processing an array with a foreach-loop:

In your test program, add a button2 with this logic:

Program 2.90 - Simple "foreach" loop, complete


private void button2_Click (object sender, EventArgs e)
{
//Declare and populate a string array:
string [] alistOfNames = {"Bob", "Tom", "Tim", "Jane", "Marge"};

foreach (string tempString in alistofNames)


{
MessageBox.Show (tempString);
}
}

where:

• The string array is arbitrarily called "alistofNames" and the compiler knows it is a
string array because of the square-brackets [ ]. I like to preface the variable name
with an "a" to remind me it is an array.

• "string [ ]" declares a string array of an undetermined size. The array will grow to
accommodate the string values inserted into it on the other side of the equal sign. In

Chapter 2 - Loops Page: 108


reality, the array is fixed to the size of the names; don't infer more capabilities into
this statement than what you see.

• On the same line, literal strings, encased in braces, insert data into the array. This is
one of several ways to put data into an array.

• Each item in the array is addressed by a numeric counter, starting at base zero.
For example, alistNames [0] = "Bob", alistNames [3] = "Jane".

"foreach" loops know how many items are in the array and the loop knows how to begin
the loop, increment and when to stop. Within the loop there are no loop-counters or
increment statements.

Because each item in the array has a unique, numbered name, the foreach loop needs to
"variablize" each item, giving it a predictable name that can be used reliably inside the
loop. The "string tempString" statement takes the indexed value and copies it to this
temporary variable.

Arrays and Lists are covered in more detail in Chapter 22 and 13.

Chapter 2 - Loops Page: 109


Loop Summary

I chose to introduce loops early in this book because they are visually interesting – and
they are definitely more entertaining than integer-size and variable-naming discussions.
Admittedly, the examples were somewhat contrived, but I hope this topic captured some
interest in programming. With this foundation you'll soon have your computer doing real
work – looping through files, modifying databases, and a host of other tasks. Loops are
the key.

If you are new to programming, this may have been a challenging chapter, with many
new concepts. Most new programmers struggle with loops and especially nested for-next
loops. The following exercises should help you understand the topic.

while-Loop Summary:

• "while" loops are useful when you don't know how many times to execute. For
example, "while not end-of-file" – where the number of records is undetermined.
The ASCII File Chapter covers this in greater detail.

while (iloopCounter <= 100)


{
//Do stuff here, then increment the loop counter
iloopCounter++;
}

and

while (EndOfFileSwitch == false)


{
//Do loop-stuff here
:
//Then retrieve next record; if end-of-file, set an EOF flag
}

• Do not use a closing semicolon on the "while" statement.

• Loops always require a pair of opening and closing braces.

• Be sure to manually increment the "while" loop's condition, which is often a counter
or some kind of boolean true-false flag. This usually happens at the end of the loop's
details. Think iloopCounter++.

Infinite loops are generally bad. However, faster computers can get through
them faster. Don't forget to increment the counter or set the conditional's
flag.

Chapter 2 - Loops Page: 110


do-Loop Summary:

do
{
<code inside of loop, including...>
iloopCounter++
} while (iloopCounter <= 100);

• "do" loops are exactly like "while" loops except they check their condition *after*
executing at least once. Note they have an unusual closing semicolon on the
conditional.

for-next Loop Summary:

for(int i = 1; i <= 100; i++)


{

• for-next loops are used when the loop count is known or when the loop count can be
pre-determined.

• for-next loops read them like this: "Start <variable i> at 1, loop while <condition> is
true; at the end of the loop, increment the variable." Unlike other types of loops, the
entire loop is self-contained in one line of code - making them succinct and easy to
interpret.

• for-next loops automatically increment themselves when they reach the closing
brace.

• Within the for-next statement, all variables should point to a <temporary> variable;
often called "i".

• Usually the for-next counter-variable is declared and initialized all in one line, within
the for-next loop using for (int i = 1; ...)

• for-next loops can start at values other than 1 and can easily increment in any
number of steps. For example, two-by-two. They can start at high numbers and
work backwards.

foreach Loops:

foreach (string strtempString in arrayName)


{
MessageBox.Show("found value is: " + strtempString);
}

• Only works with arrays, array lists, and List<T>. See the Array chapters for details.

• Note how you must create a temporary holding value and that value is used within
the details of the loop.

Chapter 2 - Loops Page: 111


• All items in the array are processed by this type of loop, even if the array is over-
allocated (see Array chapter).

• You cannot use the tempString to modify the values in the original source array.

Chapter 2 - Loops Page: 112


Exercises
Exercises are meant to be challenging. These can be solved using concepts covered in
this chapter or earlier.

A. Write a while-loop that prints your name 10 times in textBox1, one name per line. Get
your name from a separate textbox and do not hard-code your name.

Hints: You will need a carriage-return/linefeed ("\r\n") and don't forget to increment the
loopCounter.

B. Using the same while-loop in Exercise A, print the number "1.", "2.", etc, in front of your
name.

1. John Smith
2. John Smith
3. John Smith
:

Re-write the same program, again using a while-loop, counting backwards.

10. John Smith


9. John Smith
:

C. Using a for-next loop, write a program that displays the numbers 0 through 10 in a multi-
lined text box. Then write the numbers in reverse order.

D. Write a program, in the style used in this chapter, that counts from 1 to 100.
Use textBox1 to hold the multi-lined results and use textBox2 to control the loop-
increment. Instead of row++, use textBox2 to control the growth of the loop.

In other words, in textBox2 type a "2" then the loop will increment by 2:
0, 2, 4, 6... to 100. Type a 5 and the loop jumps from 0, 5, 10, 15... to 100.
Display all of the numbers in the multi-lined textBox1.

E. Using the Christmas-tree program (program 2.85), make a minor change and print the
triangle-tree up-side-down. Leave the "trunk" on the bottom.

F. Modify the Christmas Tree to print only odd-numbered columns. The first row will have
one "#", the second row will have "###" (3), the third row will have 5 pound-signs, etc.
Keep printing the tree as a triangle.

G. Using a program similar to the Christmas tree (program 2.85) and from Exercise G, build
the tree symmetrically, using 2 for-next loops that control the rows and columns.

Chapter 2 - Loops Page: 113


Use a third for-next loop to control indenting.

The only way to make this tree look presentable is to print only odd-numbered rows "*".
The first row gets 1 character; the second row gets 3 characters, etc. This way the tree
can line up. This can be handled automatically by the row-loop that increments by 2.

The tree-trunk needs to self-center; print it with two rows of "@@@"-signs.

Hint: Use a separate variable to track which row you are on when indenting. Also,
remember, when "printing", the characters on the "next" print line won't go to a new line
unless a carriage-return was used ("\r\n") – sometimes you don't want a crlf.

H. Write Exercise-G in such a way that the number of rows are adjustable with either a
variable or a text box. For example, print a 5-row-high-tree, as in the illustration above,
or set it as a 7-row tree. When this variable changes, the indents should happen
automatically without code changes and the tree-trunk should self-center under the new
tree.

This is a difficult exercise, but everything can be done with simple integer variables and
three for-next loops.

Chapter 2 - Loops Page: 114


Chapter 3 - Conditional Branching

Conditional Branching is a fancy way of saying an "if" statement: "If this is true, then do
that...". The concept was first introduced in the previous loop chapter, where each loop
contained an imbedded if-statement (do while valueA is less than 10), but the nature of
the conditional was glossed over.

Topics:

• Boolean true / false


• Comparison Operators (==, < , >= , !=)
• if-statement construction; braces and semicolon rules
• Required then's; Optional else statements
• Formatting Traps
• && (AND), || (OR), ^ (XOR)
• Nested if's, else if's
• Math.Min, Math.Max
• Compounded if-statements
• switch (Case-logic)
• .ToLower() and .ToUpper()
• goto
• Ternary (? :)

Overview:

if-statement Summary
if (valueA == valueB)
{
//"Then" statements;
}
else
{
//optional "else" statements;
}

if (valueA == 1 && valueB == 15)


{
//and-statement logic here
}

if (valueA ==1 || valueB == 1)


{
//or-statement logic here
}

Chapter 3 - If Statements and Branching Page: 117


switch-statement Summary
string myColor = "Red";

switch (myColor.ToUpper())
{
case "RED":
case "DARK RED":
//statements;
//Notes
// no {braces} in section;
// Colons, not semi-colons, on case-statements
// break; statements are absolutely required
break;

default:
//acting as an else or otherwise; optional section;
break;
}

There are two major conditional branching statements: "if" statements (which I call "if-
then" statements) and "switch" statements (also called "case" or "case-select" in other
languages).

Additionally, time will be spent on the "goto" command, which is a non-conditional


branching statement and the little-used "ternary" command.

Technically a loop is a branching statement but because how


they are used in every-day coding, I categorize them differently.
In any case, they have the same underlying construction:
through some mechanism, look at a condition and decide how to
proceed.

"Boolean" as a data-type:

When speaking about "if" statements and their brethren, you are speaking about
"boolean" terms. A boolean is a data type, like "integer" or "string," that resolves to
either 'true' or 'false'. Declare boolean variables similarly to how other variables are
declared:

bool myBooleanVariable;
bool continueReading = true;

Variables can be initialized with either a 'true' or 'false' (case-sensitive) or leave un-
initialized. Unlike other programming languages, you can not use "Y/N" or "T/F" or
"0/1" - just true/false.

Chapter 3 - If Statements and Branching Page: 118


Numeric and String Booleans

There are obvious and not so obvious ways to generate true or false values in an if-
statement. You are probably familiar with the first group of operators - such as greater-
than, less-than, and not-equal-to, as in "if valueA > valueB then...". There is a second
group of more intimidating operators, &, &&, ||, and "^".

Comparison Operators

Some of these same operators can be used to compare strings - but are more limited in
scope. More details about string comparisons are found in the next chapter.

Chapter 3 - If Statements and Branching Page: 119


Basic if-statement Construction:

When a boolean is paired with an "if-then" statement, you have a conditional branching
command. Mentally, it reads like this: "If this is true then do this stuff; else do that
stuff." You will find if-statements have a somewhat confusing set of rules when it comes
to braces and semicolons and it is further complicated by optional "else clauses," which
act like another set of statements.

When building an if-statement, the 'if' always has a pair of (parenthesis) and in the
parentheses is the conditional-test. For example, if(iValueA > iValueB). Recall
while-loops also used parenthesis in their conditionals. Following the if-statement are
one or more lines of instructions, delimited (or blocked) with a pair of {braces}.

if (iValueA > iValueB)


{

Chapter 3 - If Statements and Branching Page: 120


The if-statement ends at the closing brace and if it is false, logic jumps to this same
closing brace. You can think of the closing brace as the end-if. Visual Basic
programmers will notice there is no "end-if".

The "else" clause is optional and mentally you should treat it as its own separate
statement with its own set of braces.

Many think of if-statements in this fashion: "If this, then, else that" – a very Excel-like
mentality, where the opening brace is the "true" or "then" side.

Alternates:

Alternately, use the "Equals" method as in:

if (testString.Equals("Brown"))
or
if (Equals(testString, "Brown"))

The "Equals" method, which works on strings, integers and a variety of other types, can
be made a less more sensitive to non-English comparisons, using this syntax:

if (testString.Equals("Brown", StringComparison.CurrentCulture)).

Finally, one more introductory comment: When comparing two integers:


if (7 == 5) or
if (valueA == valueB)

you must use an equality operator of some type, "==", ">=", ">", etc.. But, if you are
testing an already-created boolean variable (such as 'myFlag', which can be True of
False, from the illustration above), then the true-false comparison is assumed. For
example, these statements are synonymous:

if (myFlag == true) if (boolVariable == true)


if (myFlag) if (boolVariable == true)
If (!myFlag) eg: "! = not" if (boolVariable != true) - !=False

where an exclamation-point ("!", often enunciated as "bang") means "not", as in


"if not myFlag".

Numeric "if" Statements:

Similar to Program 2.2, begin a new project. On Form1, place two textboxes and a
button. Modify the button1_Click event by double-clicking the button and adding the
following code. As usual, pay attention to the braces and indentation.

Chapter 3 - If Statements and Branching Page: 121


Button1 will compare two hard-coded integer variables and displays a message saying if
they are equal or not. Note: textBox1 will hold the results of the compare; textBox2 will
be used in later examples.

Program 3.1 - Numeric if, complete


private void button1_Click (object sender, EventArgs e)
{
int valueA = 10;
int valueB = 5;

if (valueA == valueB)
{
textBox1.Text = "ValueA and B are equal";
}
else
{
textBox1.Text = "ValueA and B are not equal";
}
}

Common Mistakes:

The compiler requires "double-equals" ( == ) when comparing variables. This is easy


to forget. When a single equal-sign is typed, the compiler complains with "Cannot
implicitly convert type 'int' to 'bool'"

if (valueA = 10) //Will error; needs double-equals


{
or
if (strtemp = "dog") //Will error; needs double-equals
{
:

A single equal means "gets," as in, valueA = 10, where valueA gets or is assigned the
value 10, while a double-equals means a comparison or boolean.

Why such a strange message? Here is what happens when you forget the double-equals.
The compiler sees the statement:

Chapter 3 - If Statements and Branching Page: 122


The equal-sign tells the compiler to assign "dog" to the temp-string, which results in a
string being fed into the if-statement. Using the example above, the statement converts
to "if (dog)", which is nonsensical because if-statements only know about "boolean"
True/Falses and, by design, they cannot handle the string. It wants to resolve to either
"if (true)" or "if (false)".

Different Brace Style:

Knowing that white space is ignored by the compiler, some programmers style their
opening if-statement braces at the same level as the "if". This is a legal and somewhat
common way to block your code and is common in Java scripting. The editor will
dissuade you from doing this and will try to impose a more standard design:

if (valueA == valueB) {
<do stuff here>
}
else {
<do stuff>
}

Optional Braces:

The statement following the if-clause is normally blocked with a pair of braces. But,
when an if-statement clause has only one instruction, braces are optional. These two if-
statements are functionally equivalent:

//Two if-statements; one with braces and one without:

//Style 1:
if (valueA == valueB)
{
textBox1.Text = "Value A and B are equal";
}

//Style 2:
if (valueA == valueB)
textBox1.Text = "Value A and B are equal";

In the second version, the braces were discarded – ! but this can only be done when the
if-clause has a single statement and the closing semicolon replaces the closing brace.
Braces are absolutely required If more than one statement exists inside the if-statement
clause.

Chapter 3 - If Statements and Branching Page: 123


Recommendations:

There are light-hearted arguments in the development community about having the
opening brace on the same line as the if-clause, and about not using braces on a one-line
if-statement. Obviously it saves vertical space and typing; others argue it is inconsistent.

I recommend using opening and closing braces for all blocked code – even if there is
only one line of text. And I recommend the braces typed on their own lines. This seems
to be most prevalent style and in practice, it is easy to type and read. Admittedly, it takes
more vertical space.

Recommended if-statement brace formatting


if (valueA == valueB)
{
textBox1.Text = "ValueA and B are equal";
}
else
{
textBox1.Text = "ValueA and B are not equal";
}

Consistently using braces means one-less decision while coding. In my experience, an


added second or third line of code will invariably be required so you might as well type
the braces now.

Optional "else":

With all if-statements, the first "then" clause is required but the else-side is optional.
What if your logic has nothing to do on the "then" side and all the logic needs to be done
on the "else" side? You have to fool the compiler with one of two tricks: Either put in a
non-functioning then-clause or flip the conditional around.

The following code does not have an executable {statement} after the "if" – comments
are not executable and this will fail:

if (valueA > valueB)


// do nothing - but this doesn't work; the compiler complains
else
{
MessageBox.Show("stuff to do when false");
}

Adding a "dummy" clause after the "if" solves the problem – a comment is not enough.
Everything in the braces act as a single executable statement, even if there is nothing
within. The braces replace the closing semicolon used in the single-statement example:

Chapter 3 - If Statements and Branching Page: 124


Program 3.2 - Dummy if-clause with no activity, complete
if (valueA > valueB)
{
// This is a valid Dummy if-statement
// if-clauses cannot be blank and it takes more than a comment
// to satisfy the compiler. There must be something here, an
// executable or a {block} of code with comments.
}
else
{
MessageBox.Show ("stuff to do when false");
}

or more simply:

if (valueA > valueB)


{

}
else
{
MessageBox.Show ("Stuff to do when false");
}

An alternative is to flip the conditional by using a Less-than (or "not-equal" in the case
of an equality):

Alternate Dummy-if
if (valueA < valueB)
{
MessageBox.Show("stuff to do when 'false'");
}

or

if (valueA != valueB)
{
MessageBox.Show("stuff to do when 'false'");
}

(English falls apart in this last example. It is almost like a double-negative: a not-equal
result gives the if-statement a 'true' response. Remember, if statements only think in
terms of true and false.)

if-statement Semicolon Rules:

Like the loop commands, there are no closing semicolon on the if-statement itself, but
out of habit, you may type one.

if (valueA == valueB) //No semicolon

Chapter 3 - If Statements and Branching Page: 125


When a superfluous semicolon is added, the compiler complains with several error
messages (brace expected, "Possible mistaken empty statement" and "Invalid expression
term 'else'"). Notice how the semicolon does not tuck against the if-statement.

By now you should be wondering why loop and if-statements don't use closing semi-
colons at the end of their phrase when almost every other command does. Remember
that all statements in C# are one command long and each command ends in a semi-colon.
if-statements and loops are no exception - they can only be one command long. Reading
the command in English explains why: "If valueA is equal to valueB then do this one
thing. Semicolon."

The compiler works this way for reasons only known to the compiler-gods, but for
aesthetic reasons, humans like to write the statement as a two-lined phrase with the "if"
on one line and the "then" on the next. This makes it appear as two lines when it really
is not. Of course, C# doesn't care about "white space" and the compiler treats it as one
line no matter how many physical lines you type.

With this knowledge, Program 3.1 could be written correctly, without braces. The 'else'
can be considered a separate command. Two commands; two semi-colons:

if (valueA == valueB)
textBox1.Text = "ValueA and B are equal";
else
textBox1.Text = "ValueA and B are not equal";

Because both halves of the if-statement are only one line long – and that line ends in a
semicolon – the two-line method is legal. To continue the example, you can roll up the
lines, removing all white space. For aesthetic reasons, this style is not recommended:

if (valueA == valueB)textBox1.Text = "A and B are equal";


else textBox1.Text = "A and B are not equal";

Chapter 3 - If Statements and Branching Page: 126


or to further demonstrate the lack of respect for white-spaces, the statement could be
written in this non-recommended fashion:

if (
valueA ==
valueB)
TextBox1.Text =
"A and B are equal";

Braces:

And of course the statements could be written with braces to delimit the if-clause and the
else-clause. (But as always, braces are required if the clause has more than one
statement.) This is the key to braces: They act as a single statement – think of them as a
closing semicolon for a single statement.

if (valueA == valueB)
{
//one or more statements can go here
textBox1.Text = "ValueA and B are equal";
}
else
{
//one or more statements can go here
textBox1.Text = "ValueA and B are not equal";
}

The examples above contained only one line of code for each phrase but you could put
any number of statements between the braces. The compiler treats the braces as a block
of code and internally it groups them as one logical statement, with a call to a sub-
routine. In pseudo-code, it looks like this:

if (valueA == valueB)
{ GOTO a subroutine to do other steps, then return after the closing brace; }
else
{ GOTO another subroutine and return when done; }

When compiled, the code within the braces is shuttled to a new location; then the
computer calls the subroutine and returns when done. This happens under the hood.
With this, the compiler can stick to it's rule about all statements being one line long.

Beware of Formatting Traps:

However, as mentioned before, many programmers discard the braces on single-line if-
statements. This can introduce hard-to-find bugs. Consider these examples:

If you have more than one line of code within either the "if" or "else" clause, then braces
are absolutely required. Consider this flawed code-snippet, which to a casual eye looks
perfectly logical:

Chapter 3 - If Statements and Branching Page: 127


With textBox2 indented, it looks like it belongs to the "if-then" side of the clause. When
braces are missing on the "if" side, the older versions of the compiler generated an odd
compiler error: "Invalid expression term 'else'". With Visual Studio 2017, you get a
better "} expected" along with an odd "possible mistaken empty statement.

Fix the problem by adding braces around those two lines of code on the if-side.

//This is corrected; but is cosmetically inconsistent


//with the else-clause, which does not have - or need braces:

if (valueA == valueB)
{
textBox1.Text = "ValueA and B are equal";
textBox2.Text = "This is an error";
}
else
textBox1.Text = "ValueA and B are not equal";

But watch what happens when the else-side has two or more statements, but
you forget the braces, as in this snippet:

//This is a flawed if-else statement:


if (valueA == valueB)
textBox1.Text = "ValueA and B are equal";
else
textBox1.Text = "ValueA and B are not equal";
textBox2.Text = "This is in error and you'll never know";

You will find textBox2 (underlined) runs unconditionally – regardless of the outcome of
the if-statement; it will run whether the test was true or false, even though it was 'clearly'
intended to be part of the else-clause. The trouble is, even though it "looks" like it is part
of the else-clause, it is not. Flaws like this are hard to see. In this case, the compiler will
not show an error. Half the time the if-statement takes the top route and the other half it
takes the bottom, making the error appear intermittent. The indentation fools your eye
and you will have a devil of a time finding this bug.

Chapter 3 - If Statements and Branching Page: 128


Fix these problems by adding braces around if and else-clauses,
even when not needed. For example, the braces around the if-
clause are technically redundant because that clause is only one
statement long. On the else-side, the braces are absolutely
required:

Chapter 3 - If Statements and Branching Page: 129


&& (AND) || (OR) Booleans

Boolean statements can be combined with "and"s and "or"s to make a "compound"
statement. With logical ANDS, think "if this-is-true AND that-is-true then the whole
condition is true." if (myColor == "white" AND paintType == "Latex)... then paint
the wall. Both sides of the condition must be true before the if-statement resolves to
true.

Instead of using the word "AND", C# uses the symbols "&" and "&&". Both mean
"AND" and both work similarly. Consider this sample code:

Program 3.4 - AND &&, complete


private void button1_Click (object sender, EventArgs e)
{
//AND with double-ampersands
int valueA = 10;
int valueB = 15;

if (valueA == 3 && valueB == 15)


{
textBox1.Text =
"This would resolve as true, if it were so";
}
else
{
textBox1.Text = "In this case the answer is false";
}
}

With an AND, both booleans must be true; otherwise 'else'. In the example, even though
B equals 15, A's value does not match and the statement resolves false because of the
AND.

Both halves of the AND must be Boolean – you can think of them as individual if-
statements. Ultimately, the if-statement can be reduced to this English-like text: "If true
and true then..."

Important Difference Between "&" and "&&":

There is no difference in how "&" or "&&" compute – both arrive at the same
conclusion. But the "&&" can be more efficient because it bails-out of the comparison if
a first 'false' is encountered.

Chapter 3 - If Statements and Branching Page: 130


In the example above, where A is being tested for the number 3 – but its real value is 10,
it will fail the first half of the AND. Because of the AND, the compiler knows it is
impossible for the entire statement to resolve True. With the double-&&, you are giving
it permission to immediately jumps to the else-clause. And most importantly, it does not
even bother to calculate the second half – saving a millisecond or two of CPU resources.
This may seem unimportant but it has a more practical use.

With a single ampersand ("&" vs. "&&"), both values are calculated, regardless of the
outcomes. Then they are "AND-ed". Sometimes, single-ampersands can get you in
trouble. Consider the following statement, where there is a possibility of dividing by
zero. Here the developer knew about the possibility and attempted to trap the condition
and used a single-AND:

With a single-ampersand, both sides of the AND are computed prior to deciding the "if".
The first half checks to see if valueZ happens to be zero while the second half runs the
division. Both sides are calculated before applying the AND. Since it is against the law
to divide by zero, the program literally crashes when the single-ampersand forces the
divide-by-zero.

The double-ampersand elegantly (and sneakily) avoids the problem:

Chapter 3 - If Statements and Branching Page: 131


Program 3.5 - Test for Divide by Zero, complete
:
//Shows the value of a double-ampersand if there is a possibility
//of a divide by zero...

private void button1_Click (object sender, EventArgs e)


{
//AND with double-ampersands
int valueA = 10;
int valueZ = 0;

if (valueZ != 0 && valueA /valueZ > 10)


{
//Do the calculations or other steps here
}
}

If valueZ happens to be zero, the remainder of the if-statement does not even bother to
calculate – and the divide by zero does not happen. If valueZ is greater than zero, the
first clause resolves as true and the computer then moves ahead and computes the second
phrase, as needed.

Code like this is graceful and subtle and the details can become hazy when it is pulled for
maintenance a year later; comments are certainly warranted here. However, this type of
problem (Divide-by-zero) happens frequently in programming and in one statement you
can prevent your program from crashing. The test could be re-written using a construct
called a nested-if (described in the next section). This takes more vertical space but in
some respects it is more self-documenting and easier to interpret:

if (valueZ != 0)
{
if (valueA / valueZ > 10)
{
// greater than 10 stuff
}
else
{
// less than 10 stuff
}
}

Recommendations for &&:

A double-ampersand's performance gain is small, but if it were inside a million-record


loop it would be noticeable. In general, when ANDing, you should almost always use a
double-ampersand.

Chapter 3 - If Statements and Branching Page: 132


The "|" and "||" (OR) Boolean:

The split-vertical-bar (" | ", also known as a pipe-symbol) represents a "logical OR". If
either half of the compound if-statement is true, then the entire statement is considered
true. Think, "If either this or that is true, then..."

Using the values from above, where (valueA equals 10) and (valueB equals 15) – but the
test is against a different value, this statement resolves true.

Program 3.55 - OR clause, complete


private void button1_Click (object sender, EventArgs e)
{
//This is a Double-Or (split-vertical bar) if-statement

//Again, the variables are set one way, but the example if-
//statement will test for a different value

int valueA = 10;


int valueB = 15;

if (valueA == 3 || valueB == 15)


{

}
}

The single-bar or dual-bar logic works along the same lines as the "&&" logic. As a
general rule when working with OR, always use the double-" || "'s.

To carry the example further, a test that read as

if (true || false || false)

would still resolve 'true' because the first instance and, because of the double-pipe, the
other statements would not be examined.

If using single-pipes for the three comparisons, it would still resolve as true but the
compiler is forced to compute each of the three phrases. Using the double-pipe is more
efficient.

The "^" (XOR – Exclusive OR) Boolean:

XOR is an unnatural and little-used boolean. With the "carrot" (sometimes called a "hat"
symbol), if one side or the other resolves as true, then the statement is true; this is the
same as a standard OR. But if both sides are true or both sides false, then the statement
resolves false.

Chapter 3 - If Statements and Branching Page: 133


XOR Table (boolean-result A ^ boolean-result B)

The entire statement is Phrase I Phrase II

TRUE true false

TRUE false true

*FALSE true true

FALSE false false


* this is different than a regular OR

Consider this true XOR example, where:

• A equals 10 (any number other than 3)


• B equals 15

XOR Example, result = true


private void button1_Click (object sender, EventArgs e)
{
int valueA = 10;
int valueB = 15;

//This resolves to "true":


if (valueA == 3 ^ valueB == 15)
{
textBox1.Text = "XOR: This is True";
}
else
{
textBox1.Text = "XOR: This is a false";
}
}

XOR Example; Result false


private void button1_Click (object sender, EventArgs e)
{
int valueA = 3;
int valueB = 15;

//This resolves to "false":


if (valueA == 3 ^ valueB == 15)
{
textBox1.Text = "XOR: This is True";
}
else
{
textBox1.Text = "XOR: This is a false";
}
}

Chapter 3 - If Statements and Branching Page: 134


I am hard-pressed to find a concrete example for an XOR. But if you did need this type
of logic the XOR can resolve the command with one statement vs a series of nested-if's.
If you use this construct, be liberal with documentation.

Chapter 3 - If Statements and Branching Page: 135


Nested if-statements

if-statements can be embedded within other if-statements. When you do this, indentation
is the key to human understanding, but as usual, the computer does not care about white-
space.

Consider this situation: You have three numbers (say these are product weights) and you
need to see which is the largest. (Mathematicians should refrain from laughter: what
happens if the numbers are equal? Let this slide.)

For this example, hard-code the numbers as integers to leave the demonstration
uncluttered; pretend they come from other data sources:

private void button1_Click (object sender, EventArgs e)


{
int valueA = 10;
int valueB = 5;
int valueC = 25;
string theAnswerIs;
:

Finding the largest number using if-statements is tedious:

If A > B, then check if A > C;


If A is still greater, then it must be the largest.
Otherwise, check if B > C
If B > C, then B is the largest, otherwise C is the largest.

It reads easier in code:


Program 3.6 - Nested-if Example
private void button1_Click (object sender, EventArgs e)
{
//Finding the larger of three numbers using nested-ifs

int valueA = 10;


int valueB = 5;
int valueC = 25;
string theAnswerIs;

if (valueA > valueB)


{
if (valueA > valueC)
theAnswerIs = "A is largest";
else
theAnswerIs = "C is the largest";
}
else
{
if (valueB > valueC)
theAnswerIs = "B is the largest";
else
theAnswerIs = "C is the largest";
}
MessageBox.Show (theAnswerIs);
}

Chapter 3 - If Statements and Branching Page: 136


When the if-statement resolves down the first path (when A > B), the "else" is ignored.
Try the example and test various non-equal numbers as A, B, and C.

If you had more than three values, this type of logic becomes unmanageable. In this
case, consider loading the values into an array and sorting, using concepts in future
chapters.

"else if" – Alternate Nested if:

Nesting if-statements are simply a matter of indenting and punctuation. Done well, the
code is understandable but nested-ifs can be difficult to look at when indented more than
three conditions deep. Although there is no real limit on nesting depths, indenting is
frustrating to type. There is an alternate method, called "else if", which acts like a new
if-statement.

Consider this example, which tries to determine which color was selected:

Program 3.65 - else-if, alternate design


//Finding which hard-coded color was typed by the end-user

string myColor = "Red"; //Assign the color here as-if typed


string theAnswerIs;

if (myColor == "Blue")
theAnswerIs = "Blue is my color";

else if (myColor == "Green")


theAnswerIs = "Green is the color";

else if (myColor =="Yellow")


theAnswerIs = "Stinkn yellow is the color";

else if (myColor == "Black")


theAnswerIs = "The new Black";

else if (myColor == "Red")


theAnswerIs = "Red is my color";

else
theAnswerIs = "None of these colors";

MessageBox.Show (theAnswerIs);

comments:

• Visual Basic programmers typed "elseif" – as one word – instead of "else (space) if".
The VB editor corrected the misspelling automatically, but the C# editor does not. If
you forget, the error reports "; (semicolon) expected".

Also, in the example above, I did not type opening or closing braces, saving on that
vertical space that paper books have limits about. In real life you would probably
have a liberal selection of braces scattered throughout the code. The code above
works, however, because all the statements are single-lined statements.

Chapter 3 - If Statements and Branching Page: 137


• The last statement is an "else", not an "else if", representing a "none-of-the-above"
choice. As usual, this else-statement is optional but good programming suggests
accounting for the possibility – especially in a statement such as this, where a color
like chartreuse might saunter by.

"else if" commands are somewhat of a nuisance and they take


practice to code. To me, they are hard to interpret, but at the
same time, they are better than an endless series of nested-ifs.
This example was clean and straight-forward, but by the time
you add braces and other logic, they can get almost as messy as
nested-ifs. With nested-ifs and else-if's, there are usually better
ways to code. Consider a 'switch' statement or using additional
modules and functions.

Chapter 3 - If Statements and Branching Page: 138


Math.Min - Numeric Testing for Smaller Value

Earlier, a nested-if was used to test which of the three values was the largest. C#
provides a vaguely similar function that returns the larger or smaller of two numeric
values. It is limited to testing only two values at a time, but it makes for a simpler test
than an if-statement.

Math.Min
Math.Max

Program 3.68 - Using Min Max, complete


private void button1_Click (object sender, EventArgs e)
{
//Finding the larger of two numbers using Min/Max

int valueA = 10;


int valueB = 5;
int valuex;

valuex = Math.Max(valueA, valueB);

MessageBox.Show ("The larger number is " + valuex);


}

Compare this to a similar if-statement:

:
if (valueA > valueB)
valuex = valueA;
else
valuex = valueB;
MessageBox.Show ("The larger number is " + valuex);

Chapter 3 - If Statements and Branching Page: 139


Compounding if-Statements

Compound boolean statements can be written with phrases that contain a mixture of
ANDs and ORs. The phrases are often surrounded with parenthesis to clarify the logic
or to make it easier to read. For example, consider this compound if-statement:

Example Compound Boolean


if ((Category == "A" || Category == "B") && factory == "SFC")
{

With the parenthesis, it reads this way: "If the product category were either A or B and
the factory were "SFC", then proceed with the then-clause." If the product category were
"C", the entire clause would be skipped. If the factory were any Factory other than
"SFC", the entire clause would be skipped.

The statement could also be indented in this fashion, which is easier to read and
understand:

if ((Category == "A" || Category == "B") &&


factory == "SFC")

Confusion:

Notice how the parenthesis surround the A and B tests. This is important. Using the
same logic as any mathematical calculation, parenthetical phrases are examined first, in a
left-to-right order.

Without the parenthesis, the intent can be confusing. What happens with this statement?

if (Category == "A" || Category == "B" && factory == "SFC")

The question is whether to read this as [(A or B) and SFC] or as [A or (B and SFC)]. In
this poorly-punctuated instance, read the statement left-to-right. In this case the results
would be the same with or without parenthesis but you would have to agree a non-
parenthesis statement is difficult to interpret. With compound booleans always use
parenthesis for clarity.

At some point, statements can get too complicated for most people to comprehend. Take
a look at the following, nonsensical if-statement. When statements get this involved,
consider a different construct – usually a combination of switch and nested-if-statements:

if ((Category == "A" || Category == "B") &&


(factory == "SFC" || factory == "DEN" || factory == "SEA") &&
((shipping == "RAIL" && weight > 10000) ||
(shipping == "AIR" && weight < 500)))

Chapter 3 - If Statements and Branching Page: 140


This convoluted statement has a flaw: What happens if weight is
less than 10,000 but not Air? When you have an if-statement
this complicated, there will be a better way to write the code.
The better way will be wordier but easier to follow.

Chapter 3 - If Statements and Branching Page: 141


switch Statements

In the previous nested-if example, program 3.65, the variable "myColor" was compared
against various colors - Blue, Green, Red, etc. Once the value was discovered, a text-
phrase was stored in a separate string. Finally, at the end of the routine, the string value
was regurgitated in a MessageBox. The if-logic looked like this:

string myColor = "Red";


string theAnswerIs = "";

if (myColor == "Blue")
theAnswerIs = "Blue is my color";

else if (myColor == "Green")


theAnswerIs = "Green is the color";
etc...

With each possibility, Red, Blue, Green, etc., the nested-if only looked at "myColor".
When the same variable is being compared with multiple choices, there is a better
construct called the "switch" statement. VB programmers know this as a "Select Case".
Although it has limitations, a 'switch' is easy to use and is easy to read. It can handle
dozens of separate tests, without the bother of nested-if's.

Case "Value":

Consider this code, which tests the variable "myColor" against a list of colors and reports
which was selected. Notice the individual Case-value-colon statements. If none of the
colors match the available choices, display a "None of the above" message. For
simplicity in the example, myColor is declared and initialized at the top of the program,
as a hard-coded value:

Chapter 3 - If Statements and Branching Page: 142


Program 3.7 - switch statement, complete
private void button1_Click (object sender, EventArgs e)
{
string myColor = "Red"; //As-if the user had typed this color
string theAnswerIs = "";

switch (myColor)
{
case "Blue":
theAnswerIs = "Blue is my color";
break;

case "Green":
theAnswerIs = "Green is the color";
break;

case "Yellow":
theAnswerIs = "Stinkn yellow is the color";
break;

case "Black":
case "Red":
theAnswerIs = "Red or Black is my color";
break;

default:
//acting as an "otherwise" statement...
theAnswerIs = "None of the above!";
break;
}
MessageBox.Show(theAnswerIs);

//This switch is risky - the color is case-sensitive.


//A better test is switch (myColor.ToUpper()) and "BLUE"
}

The trade-off is this: When making their comparisons, nested-ifs can look at more than
one variable. For example, nested-if's can look at myColor, the type of material, and the
day of the week; they can compare any other variable, at any time, even within a nest.
Switch-statements are restricted to a single variable, in this case, myColor.

switch-Statement Punctuation:

"switch" statements are oddly punctuated. To begin, notice none of the cases are
"blocked" – that is, they don't use braces. Compared to all other commands you've seen,
this is counter-intuitive – until you notice the colons (not semi-colons) at the end of each
"case." The colons are actually a "goto label." The "break" ends the goto-section and
tells the compiler to jump to the first statement after the switch.

If you've had prior programming experience, the implication of a goto statement explains
much about how the switch-statement works. In the example, you can think of a switch
as a "goto Green:" or "goto Red:". Goto statements are explained in more detail in the
next section, but for now, their purpose should be fairly obvious.

Chapter 3 - If Statements and Branching Page: 143


switch statements are not limited to just string values; numeric switches are also
acceptable:

Program 3.75 - Numeric switch, complete


int valueA = 5;

switch (valueA)
{
case 5:
<do stuff here>
break;

case 10:
<do stuff here>
break;
}

This example numeric-switch only considers the numbers 5 and 10. All other numbers,
and I do mean all other numbers, do not participate.

default Clause:

An optional "default" clause processes all other possibilities not met explicitly with a
case-statement; you can think of this as an "else" or "otherwise." Inserting a default into
the numeric switch example from above, substantially changes the logic: Now, all
numbers are processed - but 5 and 10 are given special consideration.

Program 3.76 - switch with default clause


int valueA = 5;
switch (valueA)
{
case 5:
<do stuff here>
break;

case 10:
<do stuff here>
break;

default:
<do stuff for all other possibilities here>
break;
}

The default clause requires a break-statement even though it is the last phrase in the
group. Technically, default can be anywhere in the construct – top, bottom, or in-
between – but common sense dictates it should be at the end.

Unlike other languages with similar verbs, there is no "do-always" clause.

Chapter 3 - If Statements and Branching Page: 144


Comparison Types - Sadly, Equalities Only :

You can only use equalities in a switch-comparison. For example, case "red": means the
case is equal to 'red' – but it will not test for 'Red' or "RED'.

Greater-than, less-than and not-equals are also not allowed. By saying case "red": you
are implying an equal sign. Unless you like compiler errors, do not type the equals (as in
case == red).

This restriction is noticeably different than other languages, such as Visual Basic, which
allows inequalities. In those languages you could, for example, switch-case on numbers
<= 10. With C#, keep in mind the :colons act as a GOTO label and case <= 10 does
not make a good destination label.

More on Required breaks:

Each "case:", including the last, must have a "break"1. This is the same type of command
as a "break" in a loop; control is passed to the switch-statement's closing brace and the
break essentially tells the compiler to GoTo the end of the switch.

The compiler does not allow logic from one clause to flow into another and the next
case-statement label is not enough to prevent it. Forgetting a break statement will fail

1
Or a "return" can be used in lieu of a break. return would exit the current method and return to
the calling routine. But you cannot use a return and a break in the same clause. Return-statements are
discussed in a future chapter.

This would be illegal syntax:


case 5:
<do stuff here>
return;
break; (Unreachable code detected)

case 10:
:

Chapter 3 - If Statements and Branching Page: 145


with a "Control cannot fall through from one case label ...". However, you can stack
two case-statements together; this is described next.

Compound case Statements:

The myColor switch example had two labels next to each other (Black and Red); this
acts as an "OR":

Program 3.77 - Compound case statements, partial


:
case "Black":
case "Red":
theAnswerIs = "Red or Black is my color";
break;

There can be no intervening instructions between the compounded case statements. In


this example, if myColor equals Black or Red, then do the code within the case. Stack as
many choices as needed in this manner.

Other languages allow you to stack you choices in this manner: case "Black", "Red":,
but this is not a valid "goto" label and the syntax is not allowed in C#.

Case-sensitive switches - .ToLower(), .ToUpper():

switch statements are case-sensitive. For example, to compare "myColor" to "Red",


"RED", or "red", you could use this syntax:

switch (myColor)
{
case "RED":
case "Red":
case "red";
<do this stuff>
break;

But this logic does not work with "rEd" and it can get cumbersome with other choices.
A better solution is to transform the switch's conditional checking for a more generic
version of "myColor." Imagine your program prompts the user for their favorite color
(and imagine they never misspell it). The comparison-logic would be smoother and more

Chapter 3 - If Statements and Branching Page: 146


accurate if the user's input were converted to all lower or upper-case. A new keyword
(more appropriately called a new "Method") is needed:

Using .ToLower( ) with a switch-statement, recommended


switch (myColor.ToLower())
{
case "red":
//Do red stuff
break;

case "blue":
//Do blue stuff
break;
}

".ToLower()" temporarily changes what ever is in the variable myColor to all lower-
cased letters. From here, each case-statement need only check for lower-cased
possibilities (e.g. "red", "green", etc.). Notice the two empty parenthesis, which are
required by the syntax. There is more on this in the next chapter.

With string switch-statements it is almost a requirement to


convert the case before comparing.

Finally, notice this about the convert ToUpper() and ToLower():

switch (myColor.ToLower())

The conversion only changes "myColor" for the duration of the switch statement. The
original myColor value"RED" remains upper-cased. Prove this to yourself by watching
the MessageBox.Show, as it displays the myColor variable; you will find it is always
cased as-it-was. The reason? The cased value was not assigned to a new variable.

Chapter 3 - If Statements and Branching Page: 147


If you wanted the variable to permanently change to (lower-case), use a command similar
to this:

myColor = "RED";
myColor = myColor.ToLower(); //Now 'red'
switch (myColor)

Trimming Text Swtiches:

When switching against strings, especially if these are values typed by end-users, it is
wise to shift the case and it would be even wiser to "trim" the results, where .trim
removes leading and trailing spaces (more on this in the next chapters). This would be a
recommended test:

switch (myColor.Trim().ToLower())
{
case "red":
:

Do's and Don'ts with switch-Statements:

switch statements have numerous rules on how they are used:

• Multiple case-commands can not be combined into one line. For example, say that
both black and red execute the same logic. You can't use this syntax:
case "black", "red":

However, the statements can be "stacked" as long as there is not intervening code
between the conditions. This works because the "case" statements are goto-
destination labels. Again, notice the colons:

case "black":
case "red":
case "dark purple":
<do the same stuff here if any of them>
break;

• C# does not allow logic from one case-statement to "flow" into another. In other
words, you can't move from blue's case-logic and expect to flow into "green's".
Break statements are absolutely required at the end of the code-sections. "default"
also needs a break.

• You can use a "goto" statement, going to another label within the case - such as
goto default; or goto blue;. Just because you can does not mean you should.

• case choices are "case-sensitive". "Red" does not equal "red". Usually this means
you should use a variable.ToUpper() or variable.ToLower(). I like using
lowercase tests because they are easier to type.

Chapter 3 - If Statements and Branching Page: 148


• Strings should almost always be Trimmed and cased in the switch statement:

switch (myColor.ToLower().Trim())

or as a two-step operation, where myColor's value is permanently changed:

myColor = myColor.ToLower().Trim();
switch (myColor)

• The internal case-destinations can only compare against hard-coded literals or


numbers; you cannot use a variable in the conditional test. For example, this is
illegal:

switch (myColor)
{
case strMainColor: //Illegal to use a variable here

case red:

However, the top switch-compare is always a variable [switch (myColor)].

• switch statements cannot use equalities or inequalities (==, >=, etc.) in the
comparisons. This is different than in other programming languages. If these types
of comparisons are needed, use if-statements.

These switch-comparisons are illegal:


int valueA = 10;

switch (valueA)
{
case <= 10:
<Illegal comparison>
break;

case > 10:


<Illegal comparison>
break;
}

switch-statement read as a "goto-like" label and this is why inequalities are not
allowed. Phrases such as "case valueA <= 10:" is more a calculation than a label.

• There is no limit to how many "case" statements you can have within a switch-block.
Switch statements can have other embedded switch statements but this is poor
programming style. Call another module from within the first switch if you need to
do this (techniques described in a future chapter).

• The "default:" section is optional but recommended. Your program should have
logic that handles unexpected tests - even if that logic is nothing more than a
displayed error message alerting of an internal problem.

• Variables within the switch-statements must be initialized with some value, even if
an empty-string "", prior to entering the statement. In the examples above,

Chapter 3 - If Statements and Branching Page: 149


"theAnswerIs" was initialized to "". This is especially true if you are not using a
default clause

Without initializing internal variables, you will get a "Use of unassigned local
variable 'theAnswerIs'" at compile-time – even if a valid switch would have been
selected. The Default-clause, where "theAnswerIs" can be set to a default, insulates
you from this problem. However, I prefer to initialize above the routine.

• Empty phrases that no action are sometimes needed. For example:

:
case "yellow":
theAnswerIs = "Yellow is the color";
break;

case "pink":
break; //execute no logic
}

MessageBox.Show ("The answer is: " + theAnswerIs);

In this snippet, if "pink" were selected, no logic was run, and more importantly, the
variable "theAnswerIs" would not be populated. A run-time error is at risk when
later MessageBox statements tried to use the variable, which never had a populated
value. This is why "theAnswerIs" variable should be pre-initialized with a default
value, even if that value is an empty string, "". Also, even if there were a default
switch, default would not run because pink was the choice, so again, the variable
"theAnswerIs" would not be set.

Syntax Errors:

Here are some of the common syntax errors you may encounter.

• Forgetting the "break" clause:

case "yellow":
theAnswerIs = "Yellow is the color";
// break;

Results: An error message: Control cannot fall through from one case label "'case
Yellow". The Yellow "case" statement is highlighted.

• Remove the parenthesis from the main 'switch' statement:


Change switch (myColor)
to switch myColor

Results: Syntax error, " ( " expected.

Exercise:

Chapter 3 - If Statements and Branching Page: 150


What is wrong with the following switch statement? The intent: Switch based on the
length of a string. This uses a text field's .Length property:

Exercise
:
switch (strfoundString.length)
{
case 1:
//do stuff here
break;
case 2:
//do stuff here
break;
default:
//do stuff here
break;
}

Compiler error: " 'string' does not contain a definition for 'length' and no extension
method 'length' accepting a first argument of type 'string' could be found (are you
missing a directive or an assembly reference?)". Solution: Capitalize ".Length"

Chapter 3 - If Statements and Branching Page: 151


switch Statement Summary:

It may seem the switch statement has too many restrictions to be useful but the statement
is succinct and easy-to-use. Most programs find a good use for it. A well-placed switch-
statement simplifies nested-ifs and other convoluted logic. A year from now, when
reviewing this code, switch statements will be refreshingly clear and easy to understand.

Here is a summary of the syntax:

Chapter 3 - If Statements and Branching Page: 152


goto Statements

The nature of goto statements has been touched upon earlier. A goto instructs the
computer to "jump" to a new location in code. The location is defined with a destination
– called a "label".

Labels are a non-executable marker in your code – a place for the goto to land on and are
punctuated with a colon:

Program 3.8 - A Shameful goto Example


private void button1_Click (object sender, EventArgs e)
{
string myColor = "Red";
string theAnswerIs;

if (myColor == "Red")
goto bypassLogic;

theAnswerIs = "Some other color";


goto allDone;

bypassLogic:
theAnswerIs = "Red is my color";

allDone:
MessageBox.Show (theAnswerIs);

comments:

goto statements often beget other goto statements. If you have one, you invariably need
another, and soon the program is a convoluted mess. Use other constructs and simplify
the logic.

goto Statement Rules:

• Do not use goto statements, if for no other reason than to avoid the scorn of fellow
programmers. This verb is considered crude and blunt2.

• goto jump labels end in a colon (not a semicolon). As you type, the compiler shifts
the label one tab-stop to the left so the label stands out from the rest of the code. The
labels themselves are "non-executable" and do nothing other than mark a place in the
program.

2
I originally wrote this sentence as "They are considered crude and blunt" but it was unclear if I were
referring to the GOTO verb or your fellow programmers. Generic pronouns are imprecise, aren't they?

Chapter 3 - If Statements and Branching Page: 153


• goto jumps cannot land in the middle of other constructs, such as the middle of an if-
statement or the middle of a loop. A goto can land just above a loop or if-statement,
or just after – as long as you haven't violated another construct's opening or closing
brace. They cannot target another module or function.

• goto jump labels must have some executable code beneath them. In the example
above, remove the MessageBox.Show line and you'll get a compiler error "Invalid
expression term "}" (closing brace).

• Unscrupulous people use goto's to bypass part of a loop. Almost always a "break" or
"continue" is more graceful. However, in the past several years, there has been
discussion in programming circles that a well-placed, simple goto can be acceptable,
perhaps as an early-exit out of a loop or other routine. I agree to this, if used with
care.

Chapter 3 - If Statements and Branching Page: 154


Ternary Operator

The last conditional statement to discuss is the seldom-used Ternary Operator,


punctuated with a "?" (question mark). This command is unusual because it uses three
operands on one line: A test-"?", a true-assignment and a false-assignment. Think of it as
a single-lined 'if-then-else' statement that works similarly to an Excel if-statement. I
generally avoid using this verb because of its rarity and it is somewhat difficult to
interpret.

Basic Syntax:

= (test or comparison) ? value-if-true : value-if-false;

Program 3.9 - Ternary Example, complete


int valueA = 10;
int valueB = 5;
string theAnswerIs;

theAnswerIs =
(valueA >= valueB) ? "Answer is A" : "It is B";

MessageBox.Show (theAnswerIs);

Results: MessageBox will show an "Answer is A" because A > B.

comments:

• Note the question-mark and the colon. Think if-then-else, much like an Excel
formula.

• The results of the test (valueA >= valueB) must resolve to a boolean.

• The two phrases after the "?" are "what to assign if True :colon, what to assign if
False". The results are sent to the variable at the front of the statement. Ternary
Operators must assign their results to another variable – in this case I used the
variable "theAnswerIs".

• You could combine the string definition for "theAnswerIs" and the Ternary into one
line, as in:

string theAnswerIs = (valueA >= valueB) ? "Answer is A" : "It is B"

I mention this command because you might see it occasionally in the wild. Although you
can accomplish several things with one line of code, the abilities of the Ternary is
limited. Unlike a standard if-statement, you cannot add additional statements within the
"then" or "else" clause.

Chapter 3 - If Statements and Branching Page: 155


Exercises

In the style used in this chapter, attach logic to "button1_Click" solving the following
problems. Have your results display either as a MessageBox or in textBox1.Text.

A. Assume your program controls a production line.

• If a product-code is "AB-123" and its weight is less than 25 grams, mark this product
as "Underweight".
• If the product is 25 grams to 30 grams, mark the product as "OK".
• If above 30 grams, mark the product for "Rework".

Write an if-statement (or series of if-statements) that processes this request.


Display the results in a MessageBox.

Hard-code the test variables at the top of the routine using these statements. For
simplicity, pretend they are input by end-users:

private void button1_Click (object sender, EventArgs e)


{
string productCode = "AB-123";
int productWeight = 22;

Be sure to test with different product-codes and weights by simply changing the hard-
coded values.

Is this type of test a good candidate for a "switch" statement? Why or why not?

What if you had several product codes with similar tests?

B. Modify the switch program 3.7 (the switch myColor program) so it tests against all
lower-cased variables – regardless of what case was used to populate the original test-
variable.

myColor = "RED";
myColor = "Red";
myColor = "rEd";

Have all possibilities properly detect as "red".

Chapter 3 - If Statements and Branching Page: 156


C. Write a program that tests a Prefix-salutation field (e.g. Mr., Mrs., Ms., Dr., etc) for valid
data. As before, simulate the user's input using a hard-coded test variable:

string prefix = "Mr."; //Change this variable to other possibilities for testing

e.g. Mr. (Pass, with no changes)


Mr (Pass, but append period "Mr.")
MR. (Pass, but change to "Mr.")
mr. (Pass, but change to "Mr.")
Mrs. (Pass, with no changes)
Xx. (Fail with error message)

If the field is valid, allow the value to pass and MessageBox what was typed.
If not in the list, display a MessageBox error of some type.

As an added feature, if the salutation were typed with improper case (all caps, lower or
mixed-case) or is missing a period (Mr vs Mr.), correct what is being tested and allow it
to pass.

Of interest: This can be solved using a switch statement or if-else statements. No other
fancy code is required. This should be challenging.

D. With the following pizza variables, display a message showing if you've built my favorite
type of Pizza. I happen to like Round Pizzas that are Medium or Large, never Small and
a Combo or Vegi are acceptable, with Thin crust.

Shape: "Square", "Round"


Size: "Small", "Medium", "Large"
Style: "Combo", "Cheese", "Vegi", "All Meat"
Crust: "Thin", "Chicago"

Hard-code the test variables at the top of the routine:

private void button1_Click (object sender, EventArgs e)


{
string pizzaShape = "Round"; //Manually change to other
string pizzaSize = "Medium"; //values in order to test
string pizzaStyle = "Cheese"; //all possibilities
string pizzaCrust = "Thin";

Then, write logic testing what was entered in the string variables. If the pizza matches
the requirements, display a messagebox saying "Yes, pizza is editable" or "No it is not".
Run a variety of different pizza-models through your logic. No matter what is entered, it
should compute to a proper Yes or No.

This can be written with nested-if and or compound if-statements.

Chapter 3 - If Statements and Branching Page: 157


Solutions:

There are many possible solutions to these problems. Here are some suggestions:

Production-Line Problem with Product-Code AB-123


if (productCode == "AB-123")
{
if(weight < 25)
MessageBox.Show("Product under weight");
else
{
if(weight >= 25 && weight <= 30)
MessageBox.Show("OK");
else
MessageBox.Show("Rework");
}
}
else
MessageBox.Show ("Not the correct Product Code");

Detecting all possible cases for Color "Red"


myColor = "rEd";

switch (myColor.ToLower())
{
case "blue":
MessageBox.Show("Blue was found");
break;

case "red":
MessageBox.Show("Red was found");
break;

case default:
MessageBox.Show("Color not in list");
break;
}

Chapter 3 - If Statements and Branching Page: 158


Prefix Validation
string strPrefix = "Mr."; //Change this to various test values

switch(strPrefix.ToUpper())
{
case "MR.":
case "MR":
strPrefix = "Mr."; //Force a known value regardless!
break;

case "MRS.":
case "MRS":
strPrefix = "Mrs.";
break
}

MessageBox.Show("The Prefix is: '" + strPrefix + "'";


//Notice how this solution corrects missing punctuation.
//Notice how this solution corrects casing problems: "mRs."

The pizza problem with two likely solutions:

if (shape == "round")
{
if (size == "medium" || size == "large")
{
if (style == "cheese" || style == "vegi")
{
if (crust == "thin")
MessageBox.Show("This is it!");
else
MessageBox.Show("The crust is wrong);
}
else
MessageBox.Show("Wrong style");
}
else
MessageBox.Show("Not the right size");
}
else
MessageBox.Show("I only like round pizzas");

Pizza Problem
if(pizzaShape == "round"
&& (size == "medium" || size = "large")
&& (style == "combo" || style = "vegi")
&& crust == "thin")

MessageBox.Show("Eatable");
else
MessageBox.Show("Send back!");

Chapter 3 - If Statements and Branching Page: 159


Chapter 3 - If Statements and Branching Page: 160
Appendixes
Appendix A - Compiler Error Messages

Alphabetic Listing

This is an alphabetic listing of various compiler messages with likely solutions. These are
from Visual Studio 2005, SP1 through VS 2014.

Errors and warnings are sorted alphabetically. Search by the first non <variable> word.
e.g. "Argument '2': cannot convert from 'double' to 'float' will be found under "cannot..."
Messages such as "The type arguments..." will be under "The"; Messages that begin with
punctuation ("; expected") are listed first.

Additional Comments on Error Messages:


If you have repaired a mis-typed line in your program but the same error message
continues to show, try the following from the editor window: Select menu choice: "Build,
Rebuild Solution".

"; expected" (semi-colon expected)


Also: Invalid Expression Term "."

Symptoms:
The compiler normally shows exactly where a semi-colon is expected and when you get this
error it is normally flagged at the very end of a line. If the compiler shows it in the middle
of a line, it can get confusing.

Problem:
This incorrectly typed command would show an expected missing semi-colon at the
Convert.ToString phrase.:
MessageBox.Show Convert.ToString(loopCounter); //missing paren

Solution:
In this MessageBox example, note that the MessageBox.Show phrase was incorrectly typed;
it is missing a set of parenthesis. This confuses the compiler like something awful. The
correct syntax is:

MessageBox.Show (Convert.ToString(loopCounter));

Problem
In this incorrectly typed command, the word "if" is 'misspelled' with a capital "I" instead of
a lower-cased "if":
If (IsBlank(testString)) //Capital "If" is wrong

A110: Database problems, various

See "A Network-related or instance-specific error occurred while establishing a connection


to SQL Server"

A constant value is expected

Common Error Messages and Solutions Appendix A: 3


Possible Solution:
In a 'switch' statement, a 'case' is using a variable instead of a hard-coded literal or a
constant. Replace the case <variableName> with case "quoted-string" or number.

A list that is this enumerator is bound to has been modified. An Enumerator can only be used if the list
does not change. (Sic)

Symptoms:
Attempting to delete an item from an array, comboBox, listBox, etc, while in the middle of
a foreach loop.

Issue:
You cannot delete an array-item while in the midst of a foreach loop.
Mark the item's position (counter) – typically in another temporary array and use a separate
loop to remove them, after the first loop completes.

A local variable named 'e' cannot be declared in this scope because it would give a different meaning to
'e', which is already used in a 'parent or current' scope...

Symptoms:
The top of the module, typically button1_Click, already has an 'e', as in "EventArgs e" and
you probably have a try-catch that also uses "(Exception e)"

Recommendations:
See button1_Click's signature line and compare it with the catch statement's signature lines

Change the "(Exception e)" to "(Exception e2)" with corresponding changes to e2.Message.
Or consider moving (most) of the logic from button1_Click to its own routine: e.g.
A100_Process(); which won't have an 'e' in its declaration.

A Namespace does not directly contain members such as fields or methods.

Possible Solution:
Do not define variables or methods (functions) above the form level; form-class level.
If you are trying to make a "global" variable, see Chapter 7.

A Network-related or instance-specific error occurred while establishing a connection to SQL Server

Symptoms:
While opening a form that uses SQL server resources.

Solution:
Confirm that the SQL Server is running and you have rights to the database.
If the SQL Server is running locally, on the LocalHost, confirm the Microsoft SQL Server
Services are started. From Windows, Start-Run, "Services.msc"; Confirm SQL Server
(SQLExpress) is started

An Error has occurred while establishing a connection to the server.


When connecting to SQL Server 2005/2008, ... (this failure may indicate) ...

Common Error Messages and Solutions Appendix A: 4


SQL Sever does not allow remote connections.
Could not open a connection to SQL Server.

See SQL: An Error has occurred while establishing a connection to the server....

An object of a type convertible to 'string' is required.

Issue:
"return" is not returning the correct 'type'.

Solution:
Examine the method's signature line to see if it returns a string, integer or other type of
object. The corresponding return statement(s) within the module must also return that same
'type'.

For example, a string function needs to return a <string> value.


It cannot return (nothing)

example code:
private string myFunction()
{
if (util.IsBlank(mystring))
return mystring;

Meanwhile, a void function can only return (nothing):

private void myFunction2()


{
stuff
return;

An object reference is required for the nonstatic field....

This is a generic error that generally means the compiler cannot find the variable or an
associated class was not instantiated.

Possible Solution:
If the variable or method in question is in a different Class, do one of the following:
a) Declare the variable as "public" or "internal" and instantiate the class within your
Form/Class using the "new" keyword. See Chapter 6, External Class Libraries, for
details.

b) Declare the variable as "public static" <string> or

c) If the method is in error, consider declaring the method as "public static...." or better
yet, "internal static" as in

internal static string B000_INILoad.B021_DiscoverINIFileName();


//returns string

Possible Solution:

Common Error Messages and Solutions Appendix A: 5


If calling a class.method within another 'sub-Class' (see Chapter 6; where cl800_Util was
placed within the PayrollTools.cs Class), there may not be a "constructor" in the new
(payroll) class and because of this, the (cl800_Util) declaration may not have a place to run.

Move the declaration into another method, directly above the Instantiation.
In simpler terms, move "cl800_Util util;" just above the line "util = new cl800_Util();"

Possible Solution:
If you have just switched a variable from a local variable to a "public static" variable, re-
compile the program using menu Build, Rebuild Solution.

Possible Solution:
Misspelled or wrong case variable name.

Possible Solution:
Especially when using a (Form's) properties. Do not use the current Form's name (it was not
instantiated within itself); instead, use "this."

ProgramGlobal.IformLeftPos = frmA000Form.Left;
ProgramGlobal.IformLeftPos = this.Left;

Note: You could also simply use "... = Left;", which is considered too vague for
most people even though the code would work.

ArgumentoutOfRangeException was unhandled

Argument out of range exception (s) are always due to an array being unalloacted, un-
available or a value [x] within square-brackets was using a larger number than the size of
the array. This always indicates a logic or counting problem and often the problem happens
at the end of a loop, where you over-shoot by one position. Remember, arrays are base-0; a
ten-item array's last position is [9].

See also "Index out of range"

If manipulating SQL data, statements, such as:


dataGridView1.Columsn[0].xxxxx = "yyyy"
may indicate the SQL Server service has not started on your development machine or the
remote SQL server is not available.

In any case, array arithmetic should be protected with a try-catch (if using a for-next loop or
are addressing [addresses] directly. Consider using a for-each loop, if logic is appropriate.

Argument '1': cannot convert from 'object' to 'string'


Also: The best overload method match for '<form(parameter)>' has some invalid arguments.

Issue:
The parameter you are trying to send is something other than a <string>; often the results
are an object-type or a "collection".

example:
frmA031CategoryAdd addCat = new frmA031CategoryAdd
(dataGridView1.SelectedRows[0].Cells[0].Value);

Common Error Messages and Solutions Appendix A: 6


...SelectedRows[0].Cells[0].Value is not necessarily a string. You can test this
by adding a .ToString() method and placing a debugging breakpoint at the statement. While
debugging, hover the mouse before the ".ToString()" method to see the value is missing
quotes – indicating it is not a string.

Possible Solution:
Convert it to a string using one of these two techniques:
... (dataGridView1.SelectedRows[0].Cells[0].Value.ToString());
... ("" + dataGridView1.SelectedRows[0].Cells[0].Value);

Argument '1' must be passed with the 'ref' keyword


Also: The best overloaded method match for '<class>(ref string, string)' has some invalid
arguments.

Solution:
When using "By Reference" (ref), both the calling and the called functions need the 'ref'
keyword. C# requires this for documentation purposes.

Example:
appendDefaultAreaCode (ref myPhoneNumber, locationDefaultAreaCode)

private void appendDefaultAreaCode


(ref myPhoneNumber, locationDefaultAreaCode)
{

Array creation must have array size or array initializer

Issue:
Sometimes you can declare an open-ended array with a simple statement, such as:
string [] afoundFields;
but if the array is used inside of a loop (while-statement), C# often requires that the array be
initialized with a starting value or by declaring a fixed array size. This is incase the while
statement never runs and downstream commands may panic.

Solution:
Initialize the array with an item count. Consider over-allocating.
string [] aFoundFields = new string [100];

Array does not have that many dimensions

Possible Solution:
Assuming a single-dimension array (a linear array),
aArrayName.GetUpperBound(0);

where (integer 0) is the first dimension of the array, "aArrayName".

Presumably you used aArrayName[x,x], when the array only had one dimension,
aArrayName[x].

'<btnClose>' is a 'field' but is used like a 'method'

Common Error Messages and Solutions Appendix A: 7


<btnClose> is a field but is used like a method

Summary:
Typed as btnClose()
Should be typed as an Event: btnClose_Click(null, null);

See also: "is a field but is used...."

Build Failed - with no compiler error messages

Likely solutions - Do all:


Close Visual Studio
Using Windows Explorer, open the Project's folder; delete all "*.suo" files
Re-Launch VS; select top-menu View, Output Window
Rebuild solution.

If still an error, look in the output Window. (See top-menu, View, Output)

Related: See "The type or namespace name 'Tasks'....

Cannot access a closed registry key

Symptoms:
Usually while performing a .GetValue(stringName)

Solution:
Move the RegKey.Close command below the GetValue statements. If the GetValues are in
a loop, be sure the Close is after the loop.

Cannot Assign to '<string name>' because it is a 'foreach iteration variable'

Symptoms:
Attempting to manipulate an array-element from within a foreach loop.

Issue:
Within a foreach loop, you cannot modify or change the values used by the foreach loop.
More to the point, you cannot transform or change the array's internal elements with a
foreach loop.

Solutions:
If you are merely trying to change the value of the array's element, move the value to a
secondary (intermediate) temp-string. Consider this example, with particular attention on
strtempString:

foreach (string strtempString in aTestArray)


{
//Note: strtempString cannot be manipulated directly
//within the loop

//Use an intermediate value to manipulate


string tstring = strtempString;

Common Error Messages and Solutions Appendix A: 8


tstring = tstring.ToUpper();
}

If your intent is to actually change the value(s) of the items in the array, you cannot use a
foreach loop. Instead, use a for-next loop.

for (int ii = 0; ii <= aTestArray.Length - 1; ii++)


{
//This actually modifies the values in the array...
aTestArray[ii] = aTestArray[ii].ToUpper();
}

Note the loop runs to the Array's length, minus-1 – a base-0 calculation
See the Array Chapter, "Transforming Array Elements" for more details.

Cannot connect to <Server> (SQL Server Management Studio)

Symptoms:
When attempting to launch SQL Server Management Studio

Possible Solution:
Are the services (Start, Run, Services.msc) "SQL Server" started?

<argument>: cannot convert from 'double' to 'float'

Solution:
A numeric parameter must be specified as a floating point number "F"
e.g.
Pen myPen = new Pen (Color.Black, 0.3) should be
Pen myPen = new Pen (Color.Black, 0.3F)

Cannot convert method group '<various: GetLength, etc>' to non-delegate type 'int'. Did you intend to
invoke this method?

Possible solution:
ilastHighlighted = myFiles.GetLength();

Did you remember the closing parenthesis?

Cannot Convert method group '<name>' to non-delegate type 'bool'. Did you intent to invoke this
method?

Possible Solution:
if using an implied comparison in an if-statement:

if (A100_SomeMethod_ThatReturns_Bool)
{
//Incorrect, missing ()
}

you forgot the parenthesis after the method name. Instead:

if (A100_SomeMethod_ThatReturns_Bool() )

Common Error Messages and Solutions Appendix A: 9


{
//corrected with ()
}

if (A100_SomeMethod_ThatReturns_Bool() == true)
{
//optional
}

Cannot currently modify this text in the editor. It is read-only

Solution:
Close the running program before attempting to modify either the code or the design-view.
You cannot edit while the program is running.

Either close the running VS program (your program) or in the Visual Studio Editor (ISE),
click ribbon-bar "Red Square" icon to abruptly close your program.

Cannot convert null to 'System.DateTime' because it is a non-nullable value type

Issue:
A DateTime method is attempting to return a null value to the calling module when only
"DateTimes" are allowed. This often happens in a try-catch error condition.

Solution:

Change the signature line from


private DateTime A100_MethodName()

to a "nullable" DateTime, where the <brackets> are required.

private Nullable <DateTime> A100_MethodName()


{
try
{
//Do stuff here
}
catch
{
return null;
}
}

Test in the calling routine using


if (returnedVariable.HasValue)

where the HasValue method only operates on items with a nullable data-type. See below for
more information on this.

Optionally, in the case of this examle's DateTime value, you could also use this command,
bypassing the Nullable solution: return DateTime.MinValue;

Solution:

Common Error Messages and Solutions Appendix A: 10


Change the original DateTime value to a "nullable" variable by using a question-mark in the
declaration.

:
DateTime? dtValue = (some date/time or null if not available);

With this, the downsteam function can return a null, if it has the need to do so.

:
if (dtValue.HasValue)
return dtValue.Value;
else
return null;

<procedureName> cannot declare instance members in a static class

Symptoms:
Usually when building a new method or function near the "static class program" / "static
void Main" class – the main driving procedure for your program. You have tried to use a
"private void <functionName>" within a "static" class.

Solution:
Consider changing
private void <functionName> to
private static void <functionName>

Cannot implicitly convert type 'int' to 'string'

Symptoms:
Code is trying to display a text message, a MessageBox, assign a text label, or assign a text
field with both text (string) data and numeric data. The numeric data refuses to cooperate.

Solution:
Use Convert.ToString on any numeric fields (or other non-string data-types) before moving
them or concatenating them to another string [field].

MessageBox.Show ("variable 'i' is set to this value: " + Convet.ToString(I));


textBox1.Text = "The number is equal to " + Convert.ToString(valueA));

also: <variableName>.ToString();

Cannot implicitly convert type 'long' to 'int' (are you missing a cast?)

Possible Solution:
Examine the return values of the command you are using. It likely is returning a 'long'
value, not an integer. The error will be flagged deep within the code, but it is the function's
(method's) signature line where you may need to make the fix.

For example:
private int A630_ReturnFileLength (string strpassedFileName)

Common Error Messages and Solutions Appendix A: 11


and: return fi.Length; <error complains here

but the fix may be changing the "int" to "long" on the Signature line.
Change "private int ..." to "private long ..."

CS0029
Cannot implicitly convert type 'string' to 'System.Windows.Forms.Label'

Solution:
Be sure to use a ".Text" when populating a label.
For example, assigning a blank string to a label:

Incorrect: lblmyField = "";


Correct: lblmyField.Text = "";

Cannot implicitly convert type 'System.Data.CommandType' to 'SystemLdata.Sqlclient.SqlCommand'

Issue:
Missing method name ".CommandType"

example code:
SqlCommand refCategoryCMD = new SqlCommand("RecordCategoryDelete");
refCategoryCMD = CommandType.StoredProcedure; //In error

Solution:
refCategoryCMD.CommandType = CommandType.StoredProcedure;

Cannot implicitly convert type 'object' to 'string'. An explicit conversion exists (are you missing a
cast?)

Symptoms:
You are using a string array and attempting to assign a value to another text field.

For example:
lblDisplay.Text = aNames[1]; //fails
MessageBox.Show(aNames[1]); //fails

Solution:
Convert to String prior to assigning. This can be done explicitly or implicitly:

lblDisplay.Text = aNames[1].ToString();
lblDisplay.Text = (string)aNames[1];
MessageBox.Show("" + aNames[1]);

Common Error Messages and Solutions Appendix A: 12


CS0029
Cannot implicitly convert type 'string' to 'bool'
Cannot implicitly convert type 'int' to 'bool'

Symptoms:
In an "if" or other conditional.

Likely solution:
Did you use a required double-equal in the conditional?
if (testString = "Smith") vs
if (testString == "Smith")

Cannot implicitly convert type 'string[*,*]' to 'string[]'

Likely explanation: A multi-dimensioned (dynamically-sized) string array was declared in a


higher scope using and later dimensioned with actual sizes:

string [,] aCollectionNames; //Declared at a higher scope

and then later, in a different method, initialize with a fixed size, as in:

aCollectionNames = new string [15,4]; //Dimensioned

The author had this error after several mistyped array definitions. But once the array was
declared, as described above, the error persisted. Finally, after selecting menu "Build,
Rebuild Solution"; the problem went away.

Cannot implicitly convert type 'string' to 'System.Windows.Forms.TextBox'

Likely Solution:
You neglected the ".Text" appendage.

For example:
Incorrect:
textBox1 = textBox1 + Convert.ToString(<variable>);

Correct:
textBox1.Text = textBox1.Text +
Convert.ToString(<variable>);

For example:
MessageBox.Show("'" + pnlCategoryCode + "'");
vs
MessageBox.Show("'" + pnlCategoryCode.Text + "'");

Cannot implicitly convert type 'System.DateTime?' to 'System.DateTime'. An explicit conversion exists


(are you missing a cast?)

Issue: You are using a Nullable <DateTime> and since a Null is allowed, you must re-
convert to the same type. This seems redundant in code because the called function may
already be returning a Date Time. Re-cast the returned value:

Common Error Messages and Solutions Appendix A: 13


Example Syntax:

DateTime dtfileDate;
dtfileDate = (DateTime)A700_ReturnFileCreateDate(textBox1.Text);

See also "Cannot convert null to 'System.DateTime' because it is a non-nullable value


type" and consider a "nullable" declaration or cast (e.g. DateTime? dtValue;)

See Chapter 24 for further discussions.

Cannot Insert an explicit value into a timestamp column (SQL)

Issue:
SQL data field was defined as 'Timestamp' but C# code is trying to insert a Date. Change
the SQL field definition to a date-time or date format.

Cannot use local variable '<variable>' before it is declared

Likely Solution:
Declare (and possibly initialize) the variable before using:
string myString = "";
if (myString = "House")

Also, you can see this message if an if-statement, either above or below the first error has a
mis-spelled "Else" (vs "else") or missing parenthesis. This may take a while to locate in
large modules.

Possible (and likely) Solution:


In the function or method, is the last "return" statement mis-spelled, as in capital-R-Return?
or is the "return" clause otherwise mal-formed.

Changes are not allowed while code is running...

Solution:
Your program is still running from your last compile (F5 / Run). Locate the program on the
task bar and close before attempting to run it again. Alternately, from the Editor, press
Shift-F5 to force-close the program.

Command Line Arguments not parsed; Command Line Arguments ignored

By default, Visual Studio will not allow passed command line arguments, even
though the Start Options are set in the Project's properties.

Symptoms:
The program will behave as if no command-line arguments were passed, especially
if you compile a Release version of the program. Make this additional change in
the program:

Common Error Messages and Solutions Appendix A: 14


In Project Properties, Security, click [x] Enable ClickOnce Security Settings.
Then click "This is a partial trust application".
Click the Advanced button
Unclick "Debug this application with the selected permission set"
Click OK
Click "This is a full trust application"

Alternately: In Project Properties, left-nav, click Security.


Uncheck "Enable ClickOnce Security Settings".

Control cannot fall through from one case label ('case "<label>":') to another

Solution:
In a 'switch' statement, a "case" statement is missing a break; command, as in

case "Green":
<do stuff here>
break;
case "Red":
<do other stuff here>
break;

<formanme> does not contain a constructor that takes 1 arguments

Solution:
The call, typically on btnFormName_Click, instantiates a new form, as in:
frmA031CategoryMaint catMaint = new frmA031CategoryMaint("");
catMaint.InstanceRef = this;
catMaint.ShowDialog();

where it is passing one parameter, in this case, null, typed as ("").

But in frmA031's constructor, at

public frmA031CategoryMaint()
{

(this example) does not show any parameters.

The method's signature line must match the calling statement's (values). The two must
match the same count of parameters.

<DataGridView> does not contain a definition for Cells and no extension method Cells accepting a first
argument....

See below: "does not contain a definition for 'Cells'....

CS1061

Common Error Messages and Solutions Appendix A: 15


'<Classname>' does not contain a definition for "<MethodName>' and no extension method
'<MethodName>' accepting a first argument of type '<ClassName>' could be found
(are you missing a using directive or assembly reference)

For example: 'MainProgram' does not contain a definition for "A000_Base' and no extnsion
method 'A000_Base' accepting a first argument of type 'MainProgram' could be found (are
you missing a using directive or assembly reference)

Likely solution:
In another class (e.g. MainProgram.cs), you have not yet created or have misspelled a
method called "A000_Base".

<formname> does not contain a definition for <event such as 'checkBox1_CheckChanged'>


<formname> does not contain a definition for <'textBox1_TextChanged'>

Symptoms:
This is an Event problem where the original Event's code was either deleted or renamed in
Code View, but the pointer to the event was not changed in the Event Properties.

Solution(s):
There are two ways to correct this error. Either is acceptable.

1. Double-click the error and the editor will take you to the [Form1.Designer.cs] class;
and as scary as this may look, delete the entire highlighted line.

2. Or, open the <event> properties (Lightning Bolt) for the control in question and delete
the event information from the property screen. For example, if this were a
textBox1_TextChanged event, delete the detail-text after the (lightning-bolt) event.
Doing so still leaves the "textChanged" code, orphaned, in the program. It should be
deleted by hand.

Common Error Messages and Solutions Appendix A: 16


<System.Windows.Forms.DataGridView> does not contain a definition for Cells and no extension
method Cells accepting a first argument....

Solution:
In the foreach clause, did you use "DataGridViewRows" (and not just "DataGridView")?

foreach (DataGridView currentRow in dataGridView1.SelectedRows)


MessageBox.Show("Selected: " + currentRow.Cells[0].Value;

Entering Break Mode failed for the following reasons: Source file <server-drive....form.cs> does not
belong to the product being debugged.

Cause:
A previous project was moved from a server-drive to a local disk.
Reference paths still point to the (old) server location.

Solution:
With Visual Studio 2005 or above, select menu Build, Clean Solution followed by Build,
Rebuild Solution.

With Visual Studio Express, these menu choices may not be present. Do the following:
a. Close the Visual Studio Project
b. Using Windows Explorer, locate the solution; delete the "bin" and "obj" sub-
directories. Re-open the Solution and the problem should be fixed.

error CS0234: The type or namespace name 'Tasks' does not exist in the namespace
'System.Threading'

Error visible in the View, Output pane.


See error message "The Type or namespace name 'Tasks'

ExecuteNonQuery: Connection Property has not been initialized.

When using a Stored Procedure and attempting a SAVE or INSERT (ADD) operation.
Missing a connection clause with the SqlCommand. For example:

Incorrect:
SqlCommand refCategoryCMD =
new SqlCommand("RecordCategoryUpdate");

Corrected:
SqlCommand refCategoryCMD =
new SqlCommand("RecordCategoryUpdate", refCategoryConn);

where "refCategoryConn" was the connection defined earlier in the routine, as in:
string strConnection = "Data Source = <servername\\SQLExpress;" +
"Initial Catalog = <database name>;" +
"User ID=<sa>; Password = <password>";
refCategoryConn = new SqlConnection(strConnection);

Common Error Messages and Solutions Appendix A: 17


<method name> hides inherited member 'SystemWindows.Forms.<object>' Use the new keyword if
hiding was intended.

example message: Form1.left(string, char)' hides inherited member


'system.windows.forms.control.left'.

Cause: The name of your function/procedure/method is the same as a built-in name. e.g., if
you built a function called "Left". This is a new warning, starting with Visual Studio 2010.

Solution:
This error can be ignored. But consider renaming your function. For example, instead of
"Left", use "LeftStr". In general, single-word functions, such as Left, Mid, Right should not
be used.

Field '<name>' is never assigned to, and will always have its default value null (warning)

Possible Solution:
A variable was declared but was never set equal to anything. The 'variable' does not need to
be a normal variable, it could be a class name. Consider this example when declaring an
external class library with the "new" statement either commented or not typed in the proper
location:

clSiteGlobals SiteGlobals;
//SiteGlobals = new clSiteGlobals();

IDE1006 Naming rule violation: These words must begin with upper case characters: <button1_Click>

This is an informational message. Rename the procedure or method, shifting the first
character to upper-case. This is to follow recommended naming standards for cross-
platform programs.

Identifier Expected

Possible Solution:
When declaring a function, are all the parameters in the parameter list prefixed with a data-
type? Missing "string", "int", etc.

Incorrect: static bool IsNumeric(passedString)


Correct: static bool IsNumeric(string passedString)

'<btnClose>' is a 'field' but is used like a 'method'


<btnClose> is a field but is used like a method

Likely Solution:
You are calling a button-event from another location but forgot or mis-typed the event-
name.
btnClose ("", null); //Incorrect – not just the btn name
btnClose_Click ("", null); //Corrected: _Click was missing

Index Out of Range (DataGridView)

Symptoms:

Common Error Messages and Solutions Appendix A: 18


Commands that use a column index position, such as

dataGridView1.Columns[0].HeaderText = "SEQ";
dataGridView1.Columns[1].Width = 55;

must be written after the statement that populates the actual grid. See Chapter 27 for
examples.
GetMyData(strSelectString)

Confirm the SQL Server (SQLExpress) services are running (Services.msc) or the remote
server is available.

See also: ArgumentoutOfRangeException was unhandled

Index was outside the bounds of the array

Symptoms:
A generated error, usually from a try-catch, where array operation attempted to access a
point not in the array – usually one position beyond the end of the array [max n + 1].

If you are not looping through the array and are directly accessing the array (e.g. variable
[n]), then likely the array was not populated with data; especially with a previous .Split
command.

Possible Diagnostics:
If using a foreach loop, place a debug break point at the top of the loop and monitor the
loop. If you suspect the error is (1000 records) into the loop, add this diagnostic logic to the
program and break within the if-statement:

foreach ....
{
if (recordCount > 999)
MessageBox.Show
("Reached suspected error; put break point here");
// <regular processing here>>
}

If using a ".Split" and a subsequent command accesses a variable-field [n] directly, likely
the split found an empty record and had nothing to split into the array. After the split, check
for blank records before executing the (parsing) logic within the loop.

Symptoms:
If you are processing a CommandLine (arguments list), what happens when no command-
line arguments are passed? If you reference aargs[1], the program would abend. Consider
this statement:

if (aargs.Length >= 2 && aargs[1].ToUpper() == "/DIAG")

where the double-ampersand is absolutely required in the test.

Index was outside the bounds of the Array (SQL)


Also: Index was out of Range

Symptoms:

Common Error Messages and Solutions Appendix A: 19


After executing a SQL statement, such as a ExecuteReader.

Possible Solutions:
Confirm the SQL Service is running (Start, Run, Services.msc; look for MSSQL/SQL
Server).

Confirm you have the proper "strConnection" (Data Source=<Franken8>)

Confirm the SQL SELECT statement (an assembled string) includes the field-name you
need and it is punctuated with appropriate commas and spaces, especially within the
assembled string.

Are you trying to reference a [column] position before a DataGridView was populated?

Invalid Expression Term ',' (plus "; expected") when using a picture clause

Likely symptoms:
You are using a picture clause (with a String.Format).

Solution:
Did you forget the words "String.Format ("?

Invalid Expression Term '{' (when using a picture clause)

Solution:
String.Format requires a string, even if a single numeric variable is being formatted.
Encompass the phrase with quotes:
textBox1.Text = String.Format ({0:dddd}, dtValue); //Incorrect
textBox1.Text = String.Format ("0:dddd}", dtValue); //Correct

Invalid Expression Term "."


See "; expected".

Invalid Expression term 'else' and ";expected"

Possible Solutions:
The "if" clause above the errored line requires braces for the "then" section. Sections with
more than one command require braces to group them.

if (valueA == valueB)
{
<stuff>
<more stuff>
}

Possible Solutions:
Does the if-statement-clause have an unneeded semicolon on the if-clause itself? Remove
the semicolon.

InvalidCastException was unhandled

Common Error Messages and Solutions Appendix A: 20


See Error: Unable to cast object of type 'System.Windows.Forms.TextBox' to type
'System.IConvertible'.

Invalid Column Name '<field>' (SQL Read)

Symptoms:
An Invalid Column Name <field name> during a SQL Read or SQL ExecuteReader and the
field name is obviously right, when examined in SQLServer Management Studio.

Possible Solution:
The assembled strSQLstmt (the SELECT statement) is mal-formed, usually a space is
missing in a quoted string, especially on the last field-name, just before the FROM clause.
Set a breakpoint at the ExecuteReader and examine the IntelliTrace. For example, in this
illustration, notice how the "FROM" is crammed next to the field "Comment":

Invalid token '{' in class, struct, or interface member declaration

This message generally means the compiler is confused about an opening or closing brace
or there is a mis-placed semi-colon that confuses where the compiler expects a brace.

Possible Solution:
You have a semicolon at the end of an if-statement; while-loop or for-next-loop or remove
an unnecessary semi-colon from the end of a statement:

Common Error Messages and Solutions Appendix A: 21


private void xxxxx (object sender, EventArgs e); (bad semicolon)
while (loop stuff); (bad semicolon)
if (condition); (bad semicolon)

Possible Solution:
There is a statement or group of statements typed after the module's closing brace. Make
sure all your code is above the closing brace (e.g. above button1_Click's closing brace).

Possible Solution:
Check to make sure that all opening braces have a closing brace and all braces are lined-up
properly. Especially near the end of the program/namespace.

Invalid token 'string' in class, struct, or interface member declaration

Likely solution:
In a statement, such as:

public string SomeMethodNameHere()

where "string" is flagged as an error.


Be sure the word "public" (private, etc.) is lower-case. Not "Public".

Login Failed for user <xxxx>. Reason: Server is in script upgrade mode. Only the administrator can
connect at this time. Error 18401.

Solution:

The SQL Server service just started and the engine is updating tables. Wait a few minutes
and try the SQL connection again.

<method> is inaccessible due to its protection level

Solution:
The method in the Class Library are "private".
Set to either:
"public" if the Class is instantiated, or if the class is in the same namespace as the calling
routine.

Set to "public static" if the Class is not instantiated and it is in another namespace.

MessageBox is a 'type' but is used like a 'variable'

Solution:
You forgot to use a dot-method with the command.
For example: MessageBox.Show (...)
where the .Show was missing

Must declare the scalar variable "@<variable name".

Common Error Messages and Solutions Appendix A: 22


Symptoms:
During a btnSave event, when using replaceable parameters

Solution:
A field was used in the UPDATE/INSERT statement but it was not defined with an
"AddWithValue" clause. For example:

refCategoryCMD.Parameters.AddWithValue
("@NonRequiredField",
util.StripSQLinjections(pnlNonRequiredField.Text));

Also check the SQLstmt in two places (once for INSERT and once for EDIT), making sure
the field-name is listed, and within the parenthesis of the field list:

strSQLstmt = "INSERT INTO refCategory " +


"(RecordCategoryCode, RecordCategoryDesc, DeleteInhibit, NonRequiredField) " +
"Values ( @CategoryCode, " +
"@CategoryDesc, " +
"@CheckBox, " +
"@NonRequiredField )";

strSQLstmt = "UPDATE refCategory SET " +


"RecordCategoryCode = @CategoryCode, " +
"RecordCategoryDesc = @CategoryDesc, " +
"DeleteInhibit = @CheckBox, " +
"NonRequiredField = @NonRequiredField " +
"WHERE (RecordCategorySeq = '" + strEditPassedRecordSeq + "')";

Newline in constant

Likely Solution:
You are appending a "\" backslash character in a string, probably to build a directory-path.
Backslash is a reserved character. Use double-backslashes to represent a single backslash.

serverName + "\" + directoryName //error


serverName + "\\" + directoryName //corrected

No overload for method '<method name>' takes 0 arguments

Likely solution:
Typically with a button or other on-screen event, such as a button, notice the signature line
of the button; there are two parameters. For example, btnSomething_Click(object
sender, EventArgs e). When calling an event like this, make your call in this fashion:

btnSomething_Click(null, null);
Passing a null value for each item in the signature line.

No overload for method '<method name>' takes '1' arguments

Solution:

Common Error Messages and Solutions Appendix A: 23


You are attempting to pass <1> parameter via the signature line to a function that either
needs more than one value or no values. In other words, the two signature lines need to
match, parameter-to-parameter. Double-click the error to locate the invalid call statement.
Then locate the routine it is calling; once found, look at its signature line (the items in
parenthesis; they must match).

Non-invocable member 'System.IO.FileInfo.Length' cannot be used like a method


Non-invocable member 'System.Windows.Forms.Control.Text' cannot be used like a method

Solution:
You are attempting to use a 'property' as-if it were a method. In other words, remove the
trailing parenthesis. For example:
fi.Length ( ); //is incorrect; use instead:
fi.Length;

or: pnlMsg.Text ("some text in quotes here"); //incorrect; use instead:


pnlMsg.Text = "some text here";

Object reference not set to an instance of an object


Use the "new" keyword to create an object instance.

This is a generic error that can be hard to resolve.

Solution:
Generally it means something is mis-spelled.

For example: RegKey.GetValue("ApplicationzzzName").ToString()


has a mis-spelled parameter.

Solution:
You are calling another method, in another library, without having first instantiating the
object. For example, when using the cl710_Formatting.cs library's "ProperNames"
function, you may need to declare the library either at the top (Class level) or within the
current function (e.g. button1_Click):

formatting = new cl710_Formatting();


then: formatting.ProperNames(<stuff>);

Solution:
You have declared a variable, such as a string, an array, a number, but have not initialized it
to a value; the variable still contains nulls.

For instance, a string declared but not initialized:


string myName;

if (myName.Length == 5) ... Generates this error

For instance:
string [] aMyArray;

with: aMyArray[1] = "Dog" will generate the error.

Common Error Messages and Solutions Appendix A: 24


In each case, initialize the variable with a non-null value before using it in any equation or
comparison. With an array, instantiate the value with the "new" keyword:

Only assignment, call, increment, decrement, and new object expressions can be used as a statement.

Symptom 1:
In a for-next statement you have mis-keyed one of the three required phrases. For example,
this statement has an error in the first phrase:
for(i; i <= 10; ++i)

Possible Solution:
you can't use a simple variable in the first part of the phrase; it must have an assignment
clause. The statement correctly typed is:
for(i=1; i <= 10; ++i)

Possible Solution:
A method, such as
sr.Close(); or
A180_ClearDateEntryFields();
was typed without opening and closing parenthesis.

Operator '&' cannot be applied to operands of type 'string' and 'string'

Solution:
Use the "+" symbol to concatenate strings. You used to be a Visual Basic programmer,
weren't you?

Operator '&&' cannot be applied to operands of type 'bool' and 'string'

Possible solution:
In a complex if-statement or while-loop clause, would an extra set of parenthesis help?

while (lineCount < linesPerPage &&


( strReadLine = myAsciiFile.ReadLine() ) != null)

Operator '==' cannot be applied to operands of type 'string' and 'method group'
Operator '==" cannot be applied to operands of type 'method group'
Operator '+' cannot be applied to operands of type 'string' and 'method group'

Likely Solution:
You forgot a "( )" after a function name.
"ToString" vs "ToString()" is commonly missed.

For example:
If using a method, such as .ToLower; as in ...textB.ToLower
did you remember the required parenthesis, as in: textB.ToLower()
if (textB.ToLower == "dog") //incorrect
if(textB.ToLower() == "dog") //correct

For example, also in a written method call:


if (A027_CheckPreviousCount == 15) //incorrect
if (A027_CheckPreviousCount() == 15) //correct

Common Error Messages and Solutions Appendix A: 25


Operator '>=' cannot be applied to operands of type 'string' and 'string'

Symptoms:
You are using a > or < conditional when comparing two strings, as in:
if (testString >= "Brown")

Solution:
You can't use >, < operators against two strings. This is different than (VB). Instead, see
string.Compare(string1, string2, T|F);
string.CompareOrdinal(string1, string2);

Operator "||" cannot be applied to operands of type 'int' and 'int'

Symptoms:
You are using an equal sign in an if-statement; you need double-equals for the comparison.
e.g.
if (passedPhoneNumber.Length = 7 || passedPhoneNumber.Length = 8)

should be:
if (passedPhoneNumber.Length == 7 || passedPhoneNumber.Length == 8)

CS0019
Operator '+' Cannot be applied to operands of type 'TextBox' and 'TextBox'
Operator '+' cannot be applied to operands of type 'System.Windows. Forms.TextBox'

Solution:
You forgot to include the object's (field) dot-property after the object's name.

Consider this code:


label1.Text = textBox1 + textBox2;
label1.Text = textBox1.Text + textBox2.Text;

CS0642
Possible mistaken empty statement

Symptom:
On an if-statement, while, or for-next statement

Likely Solution:
Although this is a warning, it is most likely a true error. Do you have a superfluous
semicolon after an if-clause, for-next, or other loop statement?
Remove the semicolon and let the next line (or the next set of braces) act as the end-of-line.

if (stringA == stringB); // <- Remove this semicolon


{

Warning: Possible unintended reference comparison; to get a value comparison, cast the left hand side
to type string

Common Error Messages and Solutions Appendix A: 26


Symptom:
When comparing a string array to a string:
if (alSomeArray[iposition] == "some fixed string")
The ~ warning appears after runtime, not during editing

Solution:
Do one of the following by casting explicitly or implicitly:

if (alSomeArray[iposition].ToString() == "some fixed string")


if ((string)alSomeArray[iposition] == "some fixed string")

Note: The error will only clear after run-time; it will not clear during the editing session
(VS2010).

Property of indexer '<class.variable>' cannot be assigned to – it is read only.


Property or indexer '<class variable>' cannot be assigned to - it is read only.

Solution:
If this is in an if-statement, did you remember to use double-equals (==)?

Solution:
In the "get/set" routines, typically in a Global External Class Library, there is not any logic
for the "set". If your intention is to make a read-only variable, either remove the logic in
your program that is trying to set the variable's value (e.g. myName = "Smith") or add an
empty-set routine, which ignores the myName = Value statement. Fixing the error is
preferable.

Property Value Not Valid (Dialog box)


Property Not Valid

Solution:
Your program is still running while trying to edit the source code. Close your running
program before changing the code or an object's property (e.g. Click the editor's ribbon
icon: "Red Square").

Send Error Report / Don't Send "Please tell Microsoft about this problem"

Symptom:
When you ctrl-alt-Break your program and your program may be in an infinite loop or
otherwise crashed. Microsoft sees this as a problem and offers to send a diagnostic error
report to Redmond. This message is annoying and should be disabled.

Solutions:
Click "Don't Send," then make this registry key change to your workstation.

Start/Run/Regedit
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PCHealth\ErrorReporting. Dword
Value: DoReport, 0 = Don't Send.

Common Error Messages and Solutions Appendix A: 27


SQL: An Error has occurred while establishing a connection to the server. When connecting to SQL
Server 2005/2008, ... (this failure may indicate) ... SQL Sever does not allow remote
connections. Could not open a connection to SQL Server.

Possible Solutions:
1) If you are connecting from a remote computer: In Microsoft's Surface Area
Configuration tool (SQL 2005), confirm that "Local and remote connections" is
selected. Choose TCP/IP

See: "C:\Program Files\Microsoft SQL Server\90\Shared\SqlSAC.exe"

2) If you are connecting from a remote computer: Consider starting the Windows Service:
SQL BROWSER

3) Does the SQL Express Server have a software Firewall installed? For example, if using
Windows XP SP2 with Windows Firewall, open the Firewall's control panel; click
Exceptions: Add this program: sqlserver.exe. Also add SQLbrowser (udp port 1434).

Other Solutions:

4) You are using the wrong server name in the connection string.

With SQL Server 2008:


string strConnection =
"Data Source = Franken8;" +
"Initial Catalog=Address;" +
"User ID=sa;Password=<yourpassword>";

With SQL Server 2005:


string strConnection =
"User ID=sa;Initial Catalog=Address;Data Source=FRANKEN8\\SQLEXPRESS";

or use ...Data Source=LOCALHOST\SQLEXPRESS If a local database

This can be especially true if you have moved your application from one computer to
another (when in development) and your Development SQL Server also moved. The
author had this problem when moving from a Desktop to a Laptop.

5) You are using the wrong Catalog (database name). e.g. from Chapter 16 "Address"

6) And of course, the wrong username and or password. If the password is encrypted; did
you decrypt it prior to executing the command?

SQL: Login Failed for user <xxxx>. Reason: Server is in script upgrade mode. Only the administrator
can connect at this time. Error 18401.

Solution:

The SQL Server service just started and the engine is updating tables. Wait a few minutes
and try the SQL connection again.

Common Error Messages and Solutions Appendix A: 28


Static member '<namespace.class.variablename>' cannot be accessed with an instance reference;
qualify it with a type name instead.

Solution:
Was the External Class Instantiated (with a "new" keyword)?
If so, remove the "static" modifier from the variable's declaration and use the "new"
variable's name as a variable prefix.

e.g. SiteGlobals = new clSiteGlobals ();


then: MessageBox.Show (SiteGlobals.CompanyName)

If the External Class was not Instantiated (using Quick and Dirty Global Variables), prefix
the variable name using the physical Class Name, as seen in Solution Explorer. Do not use
the "new" keyword.

For example:

Use:
MessageBox.Show(<namespace name.> clSiteGlobals.CompanyName);

'String' does not contain a definition for 'length' and no extension method 'length' accepting a first
argument of type 'string' could be found (are you missing a directive or an assembly
reference?)

Solution:
Capitalize the .Length property, as in:

switch (strfoundString.Length)
{
:
}

'System.Configuration.ConfigurationSettings.AppSettings' is obsolete: 'This method is obsolete, it has


been replaced by System.Configuration!
System.Configuration.ConfigurationManager.AppSettings (depricated)

Symptoms:
When attempting to use an app.config file and
MessageBox.Show (ConfigurationSettings.AppSettings ["<variable name>"]);
And yet, the statement still works properly, except for a compiler warning.

Solution:
In Solution Explorer, References, add a Reference to .NET, System.Configuration.dll
Then change the call statement to
MessageBox.Show (ConfigurationManager.AppSettings ["<variable name>"]);
See the App.Config chapter for full details.

'System.DateTime.Now' is a 'property' but is used like a 'method'

Solution:
Remove the parenthesis from the .Now. This is not Excel.

Common Error Messages and Solutions Appendix A: 29


= DateTime.Now; //Not DateTime.Now()

System.FormatException: 'Input string was not in the correct format.'

Likely solution:
You are attempting to Convert.ToInt32(textBox1.Text) and the textBox was empty or
contained non-numeric values, such as a hyphen or other character.

This is a run-time error that should have a try-catch clause.

System.InvalidCastException: 'Unable to cast object of type 'System.Windows.Forms.TextBox' to type


'System.IConvertible'.'

You will see this message if a textBox or other string field is blank and is trying to be
converted to a numeric value - Visual Studio 2012 and older.

You will also see this if Convert.ToInt32(textBox1.Text) - where the .Text property is
missing.

Note: This is a run-time error (an unhandled exception) and your program has crashed.
Provided this is not a syntax error, consider a try-catch block.

System.Windows.Forms.TextBox - various:

Issue: Likely missing ".Text" appendage.


See:
Cannot implicitly convert type 'string' to 'System.Windows.Forms.TextBox'

CS1061
'System.Windows.Forms.<field>' does not contain a definition for 'text'

Possible Solution:
Usually this means you mis-typed or more likely, mis-capitalized a field's property value.

Consider:
Field1.text (with a lower-cased .text) vs
Field1.Text

Other properties exhibit similar type messages when mis-spelled.

CS1061
'MainProgram' does not contain a definition for "A000_Base' and no extnsion method 'A000_Base' accepting
a first argument of type 'MainProgram' could be found (are you missing a using directive or
assembly reference)

Likely solution:
In another class (e.g. MainProgram.cs), you have not yet created or have misspelled a
method called "A000_Base".

'System.Windows.Forms.MessageBox' is a 'type' but is used like a 'variable'.

Common Error Messages and Solutions Appendix A: 30


Solution:
You forgot to use a method: MessageBox.Show (
e.g., you forgot the ".Show"

This is incorrect: MessageBox("Hello World");


Corrected: MessageBox.Show("Hello World");

The best overload method match for '<form(parameter)>' has some invalid arguments

See "Argument '1': cannot convert from 'object' to 'string'.

The best overloaded method match for 'string.PadRight(int,char)' has some invalid arguments

Solution:
When PadLeft or PadRight, the pad-fill is a character, not a string. Delimit a single
character with tic marks, not quotes.
e.g. strtestValue.PadRight(ipadLength, '*');

The best overload method for 'System.Windows.Forms MessageBox.Show(string)' has some invalid
arguments.

Possible Solution:
MessageBox.Show must have a string as the first item in the "show list". For example:

MessageBox.Show (comboBox1.SelectedItem); //errors; not really a string.


MessageBox.Show (Convert.ToString(comboBox1.SelectedItem)); //works

You can also trick the method by appending an empty-string before the first object being
displayed. Often, the object is converted to a string automatically, but the MessageBox
doesn't know this. Force it to get past the compiler by putting an empty-string in the front:
MessageBox.Show ("" + comboBox1.SelectedItem);

Another way around this problem is to use a ".ToString()" method. For example, with this
RegistryKey example (snippet):

MessageBox.Show (RegKey.GetValue("ApplicationName").ToString());

The class name '?' is not a valid identifier for this language

Likely Solution:
Close all frm (forms), then close and re-open the solution. It appears the development
environment can get confused, especially if you have been deleting methods.

The current project settings specify that the project will be debugged with specific security settings

Symptoms:
When using File-IO functions or Command-Line arguments (and others)

Select Project, Properties, Security


Uncheck the "Enable ClickOnce Security Settings"

Common Error Messages and Solutions Appendix A: 31


The Insert Statement conflicted with the Foreign Key constraint <key name>... (SQL)

Symptoms:
When attempting a SQL Insert where the main table has a relationship with a sub-table. For
example, adding a new NAMES record, pointing to Record Category = 1, when using:
tblNamesCMD.Parameters.AddWithValue("@RecordCategorySeq", 1);

Problem:
@RecordCategorySeq = 1
Looking in the RecordCategory table, there is not a record with a value "1"

The left-hand side of an assignment must be a variable, property or indexer.

Example Problem Statement:


if (String.Compare (strReadLine, null) = 0)

Possible Solution:
With if-statements, use a double-equal signs (not single) when comparing values; as in:
if (String.Compare (strReadLine, null) == 0)

The name 'ConfigurationManager' does not exist in the current context.

Solution:
See "System.Configuration.ConfigurationSettings.AppSettings' is obsolete: "

Common Error Messages and Solutions Appendix A: 32


CS0103
The name '<variable>' does not exist in the current context.

Symptom 1:
You have not declared the variable using "string", "int", "float", etc, as in:

string myString;
int aNumber;

Or you have declared the value as "myString" but used the variable later as "mystring"
(case-sensitive).

Or you declared the variable in another (routine or module) and that declaration is outside
the scope of your current routine/method/module. A variable was declared in another
construct (such as within a for-next loop) and that construct has ended.

Consider the integer i, which is declared as part of the for-next loop but was used in a
MessageBox outside of the loop; in this case, variable "i" was out of scope and cannot be
used.

for (int i=1; i <= 10; ++i)


{
<stuff to do>
}
MessageBox.Show ("Variable i = " + Convert.ToString(i));

Solutions vary. Check variable declarations.

Symptom 2:
Error: The name '<FixedSingle>' does not exist in the current context.
You are setting a Property incorrectly, such as
textBox1.BorderStyle = FixedSingle

Possible Solution:
The item on the Right-side of the equal sign may need to be prefixed with a property name,
as in:
textBox1.BorderStyle = BorderStyle.FixedSingle

Note: BorderStyle requires a "using System.Windows.Forms;" statement or you can fully-


qualify the name, as in:
textBox1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle

Possible Solution:
If the 'name' is a keyword-like name:
The name 'IsNumeric' does not exist.... do you need a Class Library prefix, such as:
util.IsNumeric?

The type arguments for method 'System.Array.Resize<T>(ref T[], int)' cannot be inferred from the
usage. Try specifying the Type Arguments explicitly

Issue:

Common Error Messages and Solutions Appendix A: 33


Array.Resize only works with single-dimensioned arrays. You cannot resize multi-
dimensioned arrays; you cannot resize List<T> arrays.

Solution:
Manually copy existing array to a new, larger array -- but you will have problems that the
new array will have a different name. There does not seem to be a good solution to this
problem.

The type or namespace name 'boolean' could not be found (are you missing a using directive or an
assembly reference?)

Consider this called function, which returns a boolean:


static boolean IsBlank(string passedString)

Should be typed as:


static bool IsBlank(string passedString)

C# is inconsistent in how one should spell boolean. When used in a function, use "bool".

The type or namespace name 'CurrentUser' | 'Local Machine' does not exist in the namespace
'Registry' (are you missing an assembly reference?)

Symptoms:
When attempting to read a specific registry key from the Windows Registry

Solution:
Confirm you have a "using Microsoft.Win32;" at the top of the program.
Then use this prefix in the RegistryKey command:

RegistryKey RegKey =
Microsoft.Win32.Registry.LocalMachine.OpenSubKey
(@"Software\Test");

The author is unsure why the "Microsoft.Win32.Registry" prefix is required when a "using"
statement is in place.

More generally:
You are missing a 'using' statement (e.g. using System.Management;).
if this does not resolve the problem, often you can add a new "Reference" (in Solution
Explorer). The name will usually be the same ("System.Management").

The type or namespace name 'DllImport' could not be found (are you missing a using directive or an
assembly reference?)

Possible Solution:
Add these two statements at the top of the (DllImport) class:

using System.Collections;
using System.Runtime.InteropServices;

Note "DllImport" is spelled with lower-cased -el's Dll's

Common Error Messages and Solutions Appendix A: 34


The type or namespace name 'Return' could not be found...

Solution:
Spell "return" with a lower-case 'r'.

If a Void function:
return;

If a non-Void function:
return (some-variable);

The type or namespace name 'single' could not be found (are you missing a using directive or an
assembly reference?)

Solution:
With floating point numbers,
Use Single (with a capital S) or "float" instead.

Unlike "int", Single does not have a shorter alias. Many programmers prefer "float".

The type or namespace name 'StreamWriter' / 'StreamReader' / 'WriteLine' could not be found (are
you missing a using directive or an assembly reference?)

Likely solutions:
Confirm "using System.IO;" near the top of the program.

Confirm you are using the variable name on the WriteLine method.

For example, this is incorrect:


System.IO.WriteLine("my text to write");

Use this:
myreviewFile.WriteLine...

The type or namespace name 'Tasks' does not exist in the namespace 'System.Threading'

You are likely using one of the Wait methods and System.Threading.Tasks is only available
in dot net 4.0 and higher.

Solution:
In the project, select top-menu "Project, Project Properties".
Change the target framework from .NET Framework (3.5) to version 4.0 or newer.
Re-compile.

The type or namespace 'Windows' does not exist in the namespace 'System' (are you missing an
assembly reference?) File: cl800_Util.cs

Likely solution:

Common Error Messages and Solutions Appendix A: 35


You are writing a Console application and have imported the cl800_Util library. cl800's
"Wait" routines call a System.Windows.Form module -- which Console applications do not
allow.

Recommended Solution:
Delete cl800_Util from Solution Explorer and re-add as a "Copy" (not as a link). Once
added, locate the WAIT routines and remove them from the cl800_Util library. Because
cl800 is copied, you are only damaging this program's local version. If you write a lot of
console applications and wish to continue using cl800, move the WAIT logic into its own
library.

Note: The text was changed to reflect this need. All Wait routines were moved into their
own class library. You would see this message if you tried to combine them contrary to
what the book recommends.

Related Solutions:
Console applications cannot call any "Windows-like" method. For example,
MessageBox.Show will not work in a console application. Adding a "using
System.Windows.Forms" defeats the purpose of a console application.

There were build errors. Would you like to continue and run the last successful build?

Symptoms:
When you compile (F5) your newly-written program.

Solutions:
Select checkbox "Do not show again" and click No. In other words, you would never want
to run the previous version of your code (before newly introduced bugs; you really want to
see the current bugs).

If you had already checked yes, see Tools, Options, "Projects and Solutions", "Build and
Run". Set "On Run, when build or deployment errors occur" to "Do not Launch".

Unable to cast object of type 'System.Int32' to type 'System.String' (SQL ExecuteRead)


Unable to read record (SQL)

Symptoms:
When attempting to read a SQL record.

Solution:
When assembling the SELECT statement (strSQLstmt), the "WHERE" clause's record
number (e.g. usually a SEQuence number), must be enclosed in tic-marks. For example:

Incorrect:
:
"WHERE NameSeq = " +
strEditPassedNameSeq;

Corrected:
:
"WHERE NameSeq = " +
"'" + strEditPassedNameSeq + "'";

Common Error Messages and Solutions Appendix A: 36


See also:
Invalid Column Name (SQL)

Unable to cast object of type 'System.Windows.Forms.TextBox' to type 'System.IConvertible'. When


casting from a number, the value must be a number less than infinity.

Likely Solution:
A Convert.To phrase is missing a dot-property

Consider this flawed for-next loop fragment:


for (int loopCounter = 1;
loopCounter <= Convert.ToInt32(textBox2); ....

The "Convert.ToInt32( )" does not point to a particular property.

It should read
Convert.ToInt32(textBox2.Text)

I bet you used to be a VB programmer.

Unable to Read Record (SQL)


See Invalid Column Name (SQL)
See Unable to cast object of type 'System.Int32' to type 'System.String' (SQL)

Unrecognized escape sequence

A string was found with a "\" (backslash) character. This is a reserved character needed for
"escape sequences." If you need a backslash character in a string (typically for a file-
name\path), double-up the backslashes, as in: "C:\\data\\filename.ext"

\t = tab
\r = carriage return
\n = newline
\r\n = crlf
\\ = backslash
\' = tic
\" = quote

See Appendix Special Characters for details.

Use of unassigned local variable <"myInteger"> | <"myString">, etc

Possible Solution:
In your declarations, usually at the top of your routine, a variable, such as

int <myInteger>; or string <myString>

was declared but not initialized with an explicit value. Or you attempted to use a variable
on the right-side of an (equals) statement when it has not yet been populated by another
statement earlier in the code.

Common Error Messages and Solutions Appendix A: 37


Or, the variable was declared, but because of logic, was never assigned a value before being
used in another statement or calculation.

The variable needs an initial value, either explicitly or programmatically. Remember,


declaring a variable does not initialize it..

Another likely scenario is the variable was not initialized and a "while" loop was going to
set the value but the loop never ran (or more likely, the compiler thought the loop had a
possibility of never running).

Recommendations:
Consider using this type of syntax:

int myInteger;
myInteger = 0

or declare and initialize on the same line, as in:


int myInteger = 0;

Possible Solution:
The variable was declared in another module and has fallen out of scope.

Visual Studio cannot start debugging because the debug target <your project name\bin\debug> is
missing. Please build the project and retry, or set the OutputPath and AssemblyName
properties appropriately to point at the correct location for the target assembly.

Solution:
The Program must compile at least one time without errors or you will see this message.
Delete or comment-out the line causing a compiler error.
Run the program again (even if the program does nothing but display the form)
Close the running program and re-introduce the errors. This error should go away.

Solution:
Immediately after starting any new project, press F5 to compile the first empty-screen.
Then immediately close the running program and begin your coding work.

Solution (untested):
Select Menu: Project, Properties.
Go to "Build"; check the "Output" section at the bottom
Browse to your project's main directory/path, choosing Bin\debug"; this is where the actual
exe/dll lives.

When casting a number, the value must be a number less than infinity...

See
Error: Unable to cast object of type 'System.Windows.Forms.TextBox' to type
'System.IConvertible'.

Common Error Messages and Solutions Appendix A: 38


Appendix B - Compile and Distribution

This section discusses how to compile and distribute an .EXE.

Background:

When developing and testing a program, pressing F5 (top menu Debug, Start
Debugging) compiles the program, writes a temporary executable, and then
launches that .EXE as a separate task on the Windows task bar.

On the disk, Visual Studio builds a Debug folder in the Project's directory and in
there you will find a compiled .EXE and other support files – but only the .EXE is
needed for distribution. If you compile for "Release" (described below), a new
directory, "Release" is populated similarly.

The debug version (the .exe) contains code overhead that helps you test and
develop and this version is about 10 to 15% larger than a release version.
Although you can distribute the debug version to end-users, it is not recommended.

How to Compile and Distribute EXEs Appendix B: 39


Cheap and Easy EXE Distribution:

Follow these steps to compile your program as a stand-alone executable and to


give it a formal version number and release date. The resulting .EXE is not a full-
fledged, installable program, but it can be manually distributed (without a setup
routine) and the executable can be run from a server or from a thumb-drive, etc.
This method works well in corporate environments.

1. Open your Visual Studio solution as you would normally.

2. Select top-menu Project, (project name) Properties.

a. In the Project Properties screen, click the left-nav "Application" tab.


Change the "Startup object" from "not set" to your program's main routine,
often <ProgramName.Program>.

b. Click button, "Assembly Information".

Type a short description for the program and fill out the company, copyright,
etc.
Manually set an assembly version (version number) and a file version. The
GUID is a random number, which you should leave as-is.

c. On the left-nav, select "Build".


Change the top Configuration menu from "Debug" to "Active (Release)"
Recommend leaving the platform at "Active (Any CPU)".

3. Close the Properties tab and return to the editor.


On the top ribbon, change from "Debug" to "Release".

How to Compile and Distribute EXEs Appendix B: 40


4. Build the final code by choosing top-menu "Build", then "Build <your project's
name>"

5. Once built, use Windows File Explorer to open the project's "bin\Release" folder
(for example: C:\data\Proj\VS\FileManipulation\bin\Release)

The file (e.g.) FileManipulation.exe is distributable to end users or can be


positioned on a server. The file version is visible from Windows File Explorer.

How to Compile and Distribute EXEs Appendix B: 41


Virus Risks:

EXE files placed on a file server / file share, like all executables, are susceptible to
being infected by viruses. Be sure the EXE is in a read-only directory and your
development staff does not have write-access to the EXE or any DLL's in this
directory. This includes you. Use a service account, from a secured workstation,
when updating shared executables.

Protecting executables from viruses is a real-world


problem experienced by the author. A helpdesk employee's
machine was infected and they in-turn infected a variety of
executables on a main login-script server. As each
workstation logged in, they all were infected. It wasn't
until the next day the company's virus signatures were
updated. The Author now believes it is safer to distribute
executables locally, on each workstation. This makes
house-wide infections less-likely but is more problematic
when updating.

How to Compile and Distribute EXEs Appendix B: 42


EXE Icons

The taskbar and shortcut icon will be a default Visual Studio icon and your
program deserves better. Unfortunately, you will have to create, buy or steal your
own icon. Of the three techniques, one of them requires a bit of artistry and it is,
of course the most fun.

Obviously, thieving an icon is reprehensible and can get confusing if your program
shares the same icon as another. To help, Microsoft provides a free library of
icons, which can be found in the Microsoft Visual Studio Image Library,
downloadable at this link:

http://msdn.microsoft.com/en-us/library/ms246582.aspx

As of 2015.01, this is a downloadable .zip file. Extract all


files in the archive, then tunnel to
ImageLibrary\Actions\ICO. Other ICO libraries are near-
by.

The number of (Application) icons is limited, but the number of toolbar icons is
expansive. Regardless, it provides a good starting point, especially if you want to
draw a complete set of icons, with more on this in a moment.

Icon Files:

Icon files (.ico) are peculiar because they contain multiple images, at different
resolutions and different color depths. A fully-populated icon has these images
embedded:

16 x 16, 4-bit color


16 x 16, 8-bit color
16 x 16, 32-bit color

32 x 32, 4-bit color

How to Compile and Distribute EXEs Appendix B: 43


32 x 32, 8-bit color
32 x 32, 32-bit color

48 x 48, 32-bit color

256 x 256, 32-bit

To do an icon properly, you need a full-fidelity version at 256 x 256 pixels and
another at 48 x 48 pixels, followed by progressively smaller and less-detailed
versions. You will have poor results if you take a full-sized version and attempt to
scale it down to the smaller sizes; color shading and pixellation will occur and it
is beyond the scope of this book to describe the intricacies. As you will learn,
there is an art to creating icons.

Editing Icon .ICO files

Contrary to popular belief, you cannot create icons with most photo editors and
you can't edit them properly with MSPaint (it only sees one icon within the file).
There are ways to draw an icon locally, saved as a PNG, and then upload to a
website for ico conversion, but these are generally limited to one size, one icon.

As a free solution, Microsoft recommends this web-based editor. It will not build
the larger Windows-8 style tiles, but it is generally workable:

www.xiconeditor.com:
http://msdn.microsoft.com/en-us/library/gg491740%28v=vs.85%29.aspx

Using Visual Studio to Edit Icons

Amazingly, Visual Studio, the editor, can also edit ico (icon) files, but it comes
with infuriating limitations, only editing the 16 and 32 pixel icons. And it does not
seem to give full control over color pallets.

With the editor, you can view, but not modify 48 and 256-pixel icons. Also, it
does not appear capable of building a new icon file – it only works against existing
ones, but this restriction is easily worked around.

Even with these limitations, it is worth a moment to explore. Do the following:

A. Because you cannot create a new ico file with Visual studio, you must begin your
work with an existing icon. Locate a larger, full-fidelity icon, one with multiple
icons within the file. For example, from the downloaded ImageLibrary (see
above):

How to Compile and Distribute EXEs Appendix B: 44


ImageLibrary\Objects\ico\ActiveServerPage(asp)_11272.ico

Or search C:\Windows for any *.ico files.

Protect the original file by copying the .ico to a temporary location before editing.
I recommend creating an "Images" folder within your project and storing icon and
clipart files there.

B. From any Visual Studio Project, select File, Open. Tunnel to and open the .ico file
and it will open in a tabbed-window, next to your code and form designs. The left-
nav shows each of the different sizes. Note the editing ribbon bar is only available
on 16 and 32-pixel icons.

Example Icon in Visual Studio, showing multiple sizes

Once edited, close the tab and save the changes.

Attaching Icon Files to your Project:

The icon needs to be attached in two locations: One for the file system and a
second for the running program.

How to Compile and Distribute EXEs Appendix B: 45


1. Open the top-menu, Project, (your project's name) Properties. Select left-nav
"Application". In the Resources section, set the icon file by browsing to the icon
file. Illustrated "ActiveServerPage(asp)_11272.ico". Once selected, File Explorer
will show this as the EXE's default icon and it will automatically pick the
correctly-sized icon.

2. Return to the Form Editor (Form1, Design View). In the form's properties, locate
the "Icon" setting. Browse to the same .ico file and select. Again, the editor will
choose the properly-sized icon from within the file.

How to Compile and Distribute EXEs Appendix B: 46


3. Re-compile the program for the changes using F5 or F6-re-build. The icon will
show in File Explorer, on the program's (form's) title bar, on the Task bar, and on
any desktop shortcuts created. The icon file appears as a resource in Solution
Explorer.

How to Compile and Distribute EXEs Appendix B: 47


Creating Publishing / Distribution Packages

The "cheap and easy EXE" distribution method described above is my favorite
way of distributing a compiled program, but you can build a setup.exe that
automatically installs the software and builds an un-install routine. The benefits of
this design are:

• Setup.exe installs your program in a location of your choosing


• Adds an un-install to the Control Panel's "Add Remove Software" (Programs
and Features).
• It builds a desktop icon for the current user (In Windows 8, adds a tile to the
All Apps menu.

For example, from Windows 8's Control Panel, Programs and Features:

and from the Tile screen:

Building a Distribution Package:

1. Create a Release version of your application, as described earlier in this chapter,


then close the project.

How to Compile and Distribute EXEs Appendix B: 48


2. If this is the first time building an MSI package, you must install a Microsoft-
recommended InstallShield ("InstallShield Limited Edition for Visual Studio" - a
free product from a third-party).

Flexera Software
http://learn.flexerasoftware.com/content/IS-EVAL-InstallShield-Limited-Edition-V
isual-Studio

From the web site, download and follow the instructions. Once downloaded and
installed, close and restart Visual Studio.

3. Re-launch Visual Studio, selecting


New Project, Other Project Types, "Setup and Deployment"
Choose the "InstallShield Limited Edition Project" template

For the Location, type a path that is near but separate from your original Visual
Studio Solution. For example: C:\data\Source\FileManipulation\ (Deployment),
where "Deployment" is the recommended name. As usual, I recommend leaving
Create Directory for the solution and letting the "Name" become the actual
directory.

The new solution opens into an Install Shield Wizard with a row of buttons/icons
showing each step, starting with "Application Information." This is called the
Project Assistant and if you get lost, look in Solution Explorer and double-click the
Project Assistant

How to Compile and Distribute EXEs Appendix B: 49


4. In the InstallShield wizard's first step, "Application Information", fill out your
company name, web-address, and version number (not illustrated).

5. In Step 2/Icon 2 – "Installation Requirements", choose any restrictions you may


have, such as only installable on Windows 7 or newer and choose any required
software, typically Microsoft.Net Framework version 4.x. The screen is self-
explanatory.

6. In "Application Files", rename the default [ProgramFilesFolder] from


"InstallShield" to "MyCompany" or "MyApplication". This becomes the default
installation folder.

In the right-hand Name section, "other-mouse-click" and browse to your Release


version and add your final compiled EXE to the list. Add any additional INI files,
Readme.txt, etc, in this same location.

How to Compile and Distribute EXEs Appendix B: 50


7. In the "Application Shortcuts" section, choose where you want icons built,
typically the Start Menu (In Windows 8 this is the All Programs tile screen).

8. In the "Installation Interview" section, choose options as needed. Typically:

No - Do not display license agreement


No - Do not make users type their company or username
Yes - Allow users to modify the Installation Location
Yes - Allow the user to launch the application after install

9. Finally, select top-menu "Build", "Build Solution". Note that this is not part of the
Wizard steps. This completes the MSI build.

10. In Windows Explorer, tunnel to ...\Express\CD_ROM\DiskImages\Disk1

This is your Deployment directory – not your original program solution!

Results:
Note the Setup.exe, Setup.INI and .MSI file.

This entire directory can be positioned on a Share, CD, thumb-drive, etc. and is
ready for use.

Testing:

Launch Setup.exe and allow the program to install.

How to Compile and Distribute EXEs Appendix B: 51


Results:
• In this example, installation arrives at "C:\Program Files
(x86)\MyCompany\*.exe"
• Note desktop icon (if selected)
• If Windows 8, note the All Programs Tile installed as "Launch (your program
name)"
• In Control Panel, Programs and Features (Add Remove), you can un-install.

Possible Warning:

Warning: -7235: InstallShield could not create the software identification tag
because the Tag Creator ID Setting in General Information View is empty.
ISEXP: Warning.

Solution:

This is a warning and can be ignored with no harm. It suggests files required for
automatic inventory scanning are not in place and implies a corporate install. As
of 2014.03, this design is not in wide-spread use.

The warning can be resolved in one of two ways.

1. Disable the Inventory feature: In the Deployment Package's solution, Solution


Explorer. Tunnel to "Orgainize your Setup", "General Information". Scroll
down to the Use Software Identification Tag. Set Use Tag = no

2. Or Enable the Software Inventory by filling out the fields in the "Software
Identification Tag" section.

In Solution Explorer, tunnel to "General Information"

How to Compile and Distribute EXEs Appendix B: 52


Complete these fields:
Tag Creator Name: Your business name
Tag Creator ID: (See below to generate)
– example: regid.2009-04.com.yourBusinessName

Generating a Creator Tag:

Go to this site:
Magnicomp Software Tag Maker (free)
http://www.magnicomp.com/cgi-bin/mcswtagmaker.cgi

Complete the online form and generate an XML tag file.


Download and store the Tag file in your deployment's root directory.

My tag file was named this way:


2009-04.com.keyliner\regid.2009-4.com.keyliner.examplefilemanipulation_13
96226547.swidtag

a. In your Package's "Application Files" section, add the tag file, so it installs
at the same level as your .EXE program.

b. A second copy of the tag file must also be copied, using "Application
Files": (example file name):

%PROGRAMDATA%\2009-04.com.keyliner\regid.2009-4.com.keyliner.e
xamplefilemanipulation_1396226547.swidtag

Note: The Vendor's Generated Tag webpage will have the exact link and
name you should use – cut and paste.

Your MSI package must also build the Program Data directory:
"%PROGRAMDATA%\2009-04.com.keyliner"

where %PROGRAMDATA% value is a Windows system environment


variable. On Windows Vista and later the value is usually
C:\ProgramData

Recompiling:

If your original program is pulled for maintenance or enhancements, you must


rebuild the Release version *and* rebuild the Deployment version. Remember,
your source code and the deployment solution are two different Visual Studio
projects.

There are other features, such as automatic updates when version numbers change.
This is beyond the scope of this chapter.

How to Compile and Distribute EXEs Appendix B: 53


This completes the Compile and Distribution chapter.

How to Compile and Distribute EXEs Appendix B: 54


How to Compile and Distribute EXEs Appendix B: 55
How to Compile and Distribute EXEs Appendix B: 56
Version History:

1.01 2015.04.10
Initial Release. Submitted to Wrox Publishers; declined due to length and
competition with other titles.
Advanced copy to D.Parks for review

1.02
Chapter 19 Wait States
Expanded Auto-launch example, 19.5

Chapter 20 Printing
Added reference to "Add Reference" for System.Printing on Console
apps

Chapter 23. Files


Added "Directory.Create" exception note dealing with
C:\Program Files (x86)
1.03
Split into three volumes
CH0 - 11 + Appendixes A,B (Through Multiple Forms)
CH12 - 21 ASCII - Formatting + Appendix C
CH22 - 27 Arrays - SQL + Appendix D

How to Compile and Distribute EXEs Appendix B: 57


An Absolute Beginners Guide to C# - Volume 1
Visual Studio C# 2017
Intro -Through Forms
by Tim R. Wolf
2017.06 Version 1.04
Table of Contents

9 Chapter 1 - Introduction to the Editor 3


Your First Program. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Variables and Scope. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
Working with Text Boxes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Naming Fields
Concatenation
Default Text Values

9 Chapter 2 - Introduction to Loops 45


"while" Loops. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
loopCounter
Appending a String
Incrementing a Counter
Incrementing with "++"
Concatenating to Self with "+="
MessageBoxes
Infinite Loops. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
Breaking into the Loop
"while" Loop - Printing Numbers 1-10. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
"do" Loops.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
for-next Loops. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
Carriage-Return/LineFeed (CRLF)
Variations on "for-next" Loops. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Controlling Loops with Variable textBoxes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
Interrupting Loops. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
continue;
break Statements
"while-loops and 'continue'
Nested Loops. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
foreach Loops. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
Loop Summary. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

9 Chapter 3 - Conditional Branching 117


Numeric and String Booleans. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
Basic if-statement Construction
Numeric "if" Statements
Brace Style
"else"
Semicolon Rules
&& (AND) || (OR) Booleans. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
"|" and "||" (OR) Boolean
"^" (XOR – Exclusive OR) Boolean
Nested if-statements. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
"else if"
Math.Min - Numeric Testing for Smaller Value.. . . . . . . . . . . . . . . . . . . . . . . . . . 139
Compounding if-Statements. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
switch Statements. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
Case "Value":
default Clause
Required breaks
Compound case Statements
Case-sensitive switches - .ToLower()
goto Statements. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
Ternary Operator. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155

9 Chapter 4 - Strings 163


Declaring Strings.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
Special Character Strings (Escape Codes) \t, \r\n. . . . . . . . . . . . . . . . . . . . . . . . . . 167
Reserved Backslash
Carriage-Return-Line-Feeds CRLF
Embedded Quotes
Verbatim Text Strings - @
ASCII Codes
String Concatenation. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
string.Concat ( ) and "+"
Floating Point Numbers
string.Compare( )
<string>.EndsWith. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
Detecting .XLS file Extensions
<string>.Length. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
<string>.ToUpper( ), .ToLower( ).. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
<string>.PadLeft(). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
<string>.PadLeft(int, '*');
Date Padding with Leading Zeros
<string>.Trim( );. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
<string>.TrimStart()
<string>.TrimEnd()
Trimming with other Characters
char.IsNumber ( ).. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
<string>.Replace( ). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
Character to Numeric Conversions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
Conversions with Casting
null and Empty Strings. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
String.IsNullOrEmpty( )
Null-Conditional Operator
Testing for "blank". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
if ((strtest + "").TrimStart().Length == 0)
Finding Strings - IndexOf. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Reading an Overload
<string>.LastIndexOf
<string>.Contains
Parsing and Substrings. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
<string>.Substring. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
Classic Left-strings .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
Classic Right-string. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
Mid-Strings. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
"Mid-String" for any Length Delimiter.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253

9 Chapter 5 - Numbers and Dates 265


Integers Defined. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
Floating Point Numbers Defined.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
Casting and Conversions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
Implicit Conversions
Explicit Conversions
try-catch Error Trapping. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
Converting Strings to Numeric. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
TryParse
Rounding. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
Math.Round
Truncating Decimals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
Math.Truncate
Basic Math Functions (Division). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
DivRem (Divide Remainder) / Mod. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
Math.DivRem
Mod "%"
Other Math Functions (SQRT, etc.). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
Sqrt (Square Root)
Random Numbers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
Dates. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
DateTime.Now
String.Format
Date Parts
Date Format Pictures
DateTime.TryParse. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311
UTC (Zulu) Time
Empty Dates - Nullable Dates
DateTime.Compare. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
Date.Compare: Two Files
Less Exact Date Comparisons
Rounding Dates
Optional Project: MTWRFSU.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
Optional Project: AllowByHour. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327

9 Chapter 6 - Utility Functions - Methods 333


IsBlank( ). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
IsFilled( ). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
LeftStr, RightStr and MidStr string Functions. . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
Classic LeftStr ( ) "Left-string".. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357
Left-string with String Delimiters. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
Classic Right-String with Numeric Parameters.. . . . . . . . . . . . . . . . . . . . . . . . . . . 371
Right-string using Delimiters. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374
Mid-string with Numeric Parameters. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
Mid-string Numeric Start Position but No Length (Overload). . . . . . . . . . . . . . . . 382
Mid-string with string Delimiters and NumberOfCharacters. . . . . . . . . . . . . . . . . 383
Mid-string with Two String Delimiters.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 388

9 Chapter 7 - Advanced Utility Functions 395


StripSlashes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402
StripTrailingComments. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404
StripLastCharacter.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411
StripNonNumerics ( ). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
StripNonNumerics, Preserving Decimals and Signs. . . . . . . . . . . . . . . . . . . . . . . . 424
Overloading
ParseBetweenDelimiters. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
ParseKeyValue. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438
Overloading ParseKeyValue. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445
ParseKeyName: Master Function. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457
IsNumeric ( ). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463
IsNumbers ( ).. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473
StripDuplicateCharacters. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 475
VerifyYN.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 478
Passing Variables by Reference. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 481

9 Chapter 8 - Class Libraries 493


Building an External Class Library from Existing Code. . . . . . . . . . . . . . . . . . . . 496
Linking an Existing External Class Library. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502
Using the util. Class Library (CL800 Util). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509
Link vs Copy
Inline "Program" Class Libraries (PayrollTools). . . . . . . . . . . . . . . . . . . . . . . . . . 513
Using CL800_Util within the new Class.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 520
Constructors
Manually Building a Class Constructor
Creating an "External" Class Library from Scratch. . . . . . . . . . . . . . . . . . . . . . . . 525
Compiling and Using DLLs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526
Using the DLL
"Modularizing" with Program-Control Functions.. . . . . . . . . . . . . . . . . . . . . . . . . 532
"void" Functions
Passing Variables to Program-Functions
Returning Values from a Program-Function
Naming Standards for Functions and Class Libraries.. . . . . . . . . . . . . . . . . . . . . . 535
Object Prefixes

9 Chapter 9 - Variable Scopes 543


Variable Scope, Demonstrated. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 547
Scope within Loops
Form-Level (Class) Variables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 551
"Global" Variables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 554
"Quick and Dirty" Global Variables
"static" Modifier
Form1 to Open Form2
Using a Global Class to Pass Values.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 564
Building an External Global Class Library. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 568
Getting and Setting Variables. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 577
Building Get/Set Properties
Passing Variables by Ref.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 582

9 Chapter 10 - Form Controls and Events 589


Default Editor Settings - Recommended. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 594
Snap To Grid
Compiler Errors
Starting and Naming a New Application. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 598
Rename the Form
Recommended Form Properties
"Form Load" event
"this.Show"
Link the CL800_Util Library
AutoClose Events. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 609
textBoxes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 613
MaxLength
Default Text Value
Enabled
Password Fields
Multiple Lines
Setting Field Properties at Runtime. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 619
textBox Events. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625
Enter and Leave Events
TextChanged
KeyPress Events (Intercepting Keystrokes). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629
Allowing only Numeric Values
UpperCasing as Typed
Intercepting Shift, Alt and Ctrl Keystrokes
Alt-Key events on a button or other object
comboBox and listBox. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 638
Static (Hard-Coded) ComboBoxes
AutoComplete / Type-Ahead
Query the Selected Value
"No Selection"
Using the SelectedIndexChanged
ComboBoxes linked to an External Data
listBox. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 648
Blank-line Testing
listBox Multiple Selection
Processing Multiple Selected Records
checkBoxes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 660
ThreeState
CheckChanged
CheckStateChanged
Click
Radio Buttons. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669
Radio Button Events
Grouping
ProgressBar. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 676
monthCalendar and DateTimePicker. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 681
.MaxDate
.MinDate
Form Load Event
Date-Time Math
MenuStrip. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 689
Horizontal and Vertical Lines. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 692
ToolTips. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 694
ToolBox: "PictureBox" Icons and Buttons. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 697
Launching Other Applications From the Graphic
Link Labels. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701
Label Tricks.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 704
Transparent Labels
Using Labels in Your Program
Tab-Order. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 706

9 Chapter 11 - Calling Secondary Forms 711


Opening Secondary Forms - Simple. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 716
Issues with a Simple Form Call
Using a Global Variable to Re-Open Form1.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 724
Using a FormReference to Open Form2 - Recommended. . . . . . . . . . . . . . . . . . . 727
"FormRef" properties
Modal and Modeless Forms. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 735
Transferring Data Between Forms using Global Variables. . . . . . . . . . . . . . . . . . 737
Using Quick-and-Dirty Global Variables
Using a Global Class (Global Variables)
Retrieve the Stored Value in Form2
Passing Data with a Signature and a Constructor. . . . . . . . . . . . . . . . . . . . . . . . . . 741
The Parent's Call
The Child (Form2) Constructor
Passing Values using get-set Properties - Recommended. . . . . . . . . . . . . . . . . . . . 745
Form Starting Positions - Global Class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 753
MessageBox Overloads. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 756
Custom Dialog Boxes: NS810_Dialog. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 759
Returning Results to the Parent via Properties
Custom InputBoxes: NS815_InputBoxDialog. . . . . . . . . . . . . . . . . . . . . . . . . . . . 777
Detecting a KeyPress ENTER Key Event.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 785
Using NS815 to Pass Arrays instead of Strings. . . . . . . . . . . . . . . . . . . . . . . . . . . 787
Multiple Input Screens in the Same Program. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 789

9 Appendix A - Compiler Error Messages 3

9 Appendix B - Compile and Distribution 38


Cheap and Easy EXE Distribution:.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Virus Risks
EXE Icons. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Using Visual Studio to Edit Icons
Attaching Icon Files to your Project
Creating Publishing / Distribution Packages.. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
Generating a Creator Tag
Absolute Beginner's Guide to C-Sharp - ABGC
Published by Tim R.Wolf, © 2017
Chapter 04 - Strings
Chapter 4 - Strings

"Strings," which have already been used in earlier chapters, are nothing more than a
'string' of character data. Examples include words and phrases, as well as non-numeric
data, such as "123 N. Elm Street", phone numbers, part numbers, and the like. This
chapter describes basic and advanced string manipulation techniques.

In my experience, string manipulation is the most useful


concepts in this entire book and this is where my applications
typically spent most of their energy. Dissecting and
manipulating strings are what most programs do for a living.

Topics:

• string declarations
• \escape codes, special characters, @verbatim strings, ASCII strings
• string.Concat and + - appending/ Concatenation
• string.Compare(A, B, true)
• .EndsWith
• .ToLower(), .ToUpper()
• .PadLeft(), .PadRight()
• .Trim() - for lobbing-off leading spaces and other characters
• .TrimStart(), .TrimEnd()
• .Length
• Character arrays

• null and empty strings tests using "+"").TrimStart( ).Length > 0"
• Testing for Blank/empty data
• .IndexOf("=",0) - Finding string positions

• Overloading, introduced
• .LastIndexOf - searches from the back-end forward
• if(<string>.Contains("="))
• IsNumeric, limitations

• .Substring
• (LeftString, RightString, MidString)
• Substring with multiple delimiters

Many of the examples in this chapter use the


MessageBox.Show (<some string value here>) to display the results of a string
manipulation. The MessageBox is a simple dialog box that appears on the screen and is
being used as a diagnostic and teaching tool. Naturally, the results could be put in other
locations.

Chapter 4 - String Manipulation Page: 163


Common string Commands, summary
<string>.ToUpper() //Shift to uppercase
<string>.ToLower() //Shift to lowercase

<string>.PadLeft(<int>, '*') //Pad with 'char' for total len.


<string>.Trim() //Trims leading and trailing
<string>.TrimStart() //Trims only leading
<string>.TrimEnd() //Trims only trailing

<string>.Replace("AB", "xy") //Replace "AB" with "xy"

Convert.ToString(integer-number) //Convert number to string


Convert.ToInt32(<string>) //Convert string to number
Convert.ToSingle("123.32") //Convert to floating point

string Search and Positions, summary


int = <string>.Length //base-1 length of a string

int result = String.Compare(strA, strB, true)


//Compares <, = > on strings.
//result: 0=equal, 1=GT, -1=LT
//true = ignore case

if (String.IsNullOrEmpty(<string>) //Recommend using util.IsBlank()

if ((testString + "").TrimStart().Length > 0)


//Test for null, empty,
//all-space
//Recommend using
//util.IsBlank()*

int = <string>.IndexOf("=", 0) //Searches for "string" and


//returns base-0 count,
//starting position zero.

int = <string>.LastIndexOf("=") //Searches from the end


//for "=";
//returns base-0 count.

if (<string>.EndsWith(<"value">)) //Returns boolean


if (<string>.Contains("=")) //Searches and returns T/F

*Function from Chapter 6

Chapter 4 - String Manipulation Page: 164


Manual Left, Right and Midstrings, summary
<string>.Substring (<start position base-0>)
<string>.Substring (<start position>, <number of characters>)

Left Strings always start at position zero:


<string>.Substring (0, 10) //Left 10 Chars.
<string>.Substring (0, delimiter-foundNDX).Trim()

Right Strings:
<string>.Substring (<string.Length> - 10).Trim()
//Right 10 Chars.

<string>.Substring (<delimFrontNDX> + <delimFront>.Length).Trim();


//Right, after delim

Mid Strings:
<string>.Substring (<start position base-0>,
<number of characters>)

<string>.Substring
(delimiterFrontNDX base-0 + delimiterFront.Length,
<number of characters>)

<string>.Substring (delimiterFrontNDX base-0 +


delimiterFront.Length,
delimiterBackNDX - delimiterFrontNDX -
delimiterFront.Length).Trim()

//See also the util.Left, .Right, .Mid commands in cl800_Util.

(Split) //See Chapter 10

if (Char.IsNumeric('<single-byte char-value>'))
if (Char.IsNumeric("string value", #-of-characters))

//See util.IsNumeric for strings.

•Setting up Example Programs for this Chapter:

For the remainder of this chapter, practice the examples by building a test program
similar to the ones used in prior chapters. All examples below use a screen with
"button1" and (sometimes) textBox1. See Chapter 1 for instructions.

Chapter 4 - String Manipulation Page: 165


Declaring Strings

String variables must be declared (defined) before they can be used. Do this with a case-
sensitive "string" command. The keyword "string" declares what type of variable is
being declared, followed by the name of the variable.

string <invented variable name>;

String variable names can be any single word or phrase, with no spaces. The name you
assign is an invented name of your choosing. This author likes to prepend the string-
name with "str" as in "strtestVariable".

Optionally, the variable can be initialized with a default value on the same line, or it can
be assigned a value separately with a different statement. Use quotes to surround hard-
coded text strings (also called a 'literal').

Examples:
string strtestVariable;
string strtestVariable2 = ""
string strtestVariable1 = null
string strtestVariable3 = "123 N. Elm Street";
string strtestVariable4 = textBox1.Text;
string strtestVariable5 = strtestVariable4;

strtestVariable = DateTime.Now.ToShortDateString();

A string can be declared as an empty-string (quote-quote) – meaning a string was


assigned, but no (real) value was applied. This is different than a "null" string or an
uninitialized string with more on this later in the chapter. Strings can also be assigned
values from other strings or from a textBox.Text or they can be populated from the
results of other functions or methods.

Chapter 4 - String Manipulation Page: 166


Special Character Strings (Escape Codes) \t, \r\n

Tabs, carriage-return-line-feeds, reserved characters, and other white-text are also strings
but these cannot be typed directly into the program's code. In other words, how do you
type a "tab" without invoking the keyboard's tab and how do you place a quote-mark
within a quoted string?

To work around this, some strings are represented in the editor with special characters,
called "escape codes".

Tab \t (ASCII 9)
Backslash \\ (ASCII 92)
Double-Backslash \\\\ (ASCII 92, 92)
Double Quote \" (ASCII 34)
Single-quote (tic) \' (ASCII 39)
Carrage-Return-LineFeed \r\n (ASCII 13, 10)
Unicode \uxxxx where xxxx = unicode number

These characters are typed as 'clear text' (typed in the editor as a literal value) with a
leading backslash (" \ "), signifying their special nature. For example, a tab is
represented with "\t". Although the "\t" is typed as two characters, the compiler treats
it as a single character – a tab.

string strtabExample = "\tJohn Smith";


string strtabExample2 = "John\tQ.\tSmith";
string strmultiLineField = "four score \r\n and seven years";

Thus, "\tJohn Smith" equals "tab John Smith"


and "John\t\Q.\tSmith" equals "John tab Q. tab Smith".

Reserved Backslash:

The backslash (" \ ") is reserved as an escape sequence marker. If that actual character is
needed in a string, use double-backslashes (" \\ "), again where two typed characters
represent a single character. Since double-backslashes are used in server and share
names, those would require four backslashes (" \\\\ "):

string strserverName = "\\\\payroll\\shareName"; e.g. \\payroll\sharename


string strinputFileName = "C:\\Data\\test.txt"; e.g. a DOS Path

This can get you in trouble if you make a mistake. Consider this flawed string:

string strinputFileName = "C:\\Data\test.txt";

where the embedded slash-t (\test.txt) was intended as a DOS path but was interpreted as
a tab – "C:\Data (tab\t) est.txt". In this case, the editor would not have flagged this as an
error.

Chapter 4 - String Manipulation Page: 167


See below for Verbatim text strings for additional information.

Carriage-Return-Line-Feeds CRLF:

Carriage-return-line-feeds (hard-returns or sometimes called CRLF) are represented by


this sequence: "\r\n" – a return plus a line feed, for a total of two characters. Escape
codes can be embedded directly in a quoted string or they can be assembled with
concatenation:

"four score \r\n and seven years" or


"four score" + "\r\n" + " and seven years"

Results - noting the line break and the word " and", with a leading space:
four score
and seven years

In the second half of the example, three literals were combined into one string, using a
"+" (concatenation) and both examples are functionally equivalent. Concatenation is
discussed in a few pages.

Alternately, Microsoft recommends:


Environment.NewLine

as in: "four score " + Environment.NewLine + "and seven years".

where "NewLine" is case-sensitive. Environment.NewLine theoretically translates into


other languages with less effort. See also ASCII Codes, below.

Embedded Quotes:

Since strings are usually initialized with a quoted literal (e.g. "Bob said"), an embedded
quote-mark ( " ) cannot be placed in the middle of the string. In these cases, use an
escape character: backslash-quote ( \" ).

Although this is somewhat hard to read, the assembled string starts with a regular quote,
followed by a backslash-quote. Note how the end of the string is delimited:

Chapter 4 - String Manipulation Page: 168


Verbatim Text Strings - @:

Character strings with embedded backslashes are difficult to read and many developers
choose an alternate technique called a "verbatim" literal. Preface the quoted string with
an at-sign (@) and spell it in the 'natural' way:

If embedded quotes or white-space characters such as a tab are needed, you


cannot use verbatim strings.

Normally, a literal cannot span multiple lines in the editor but with verbatim strings you
can. The results can be weird. Note how the following string has its closing semi-colon
on the third line:

string strlongString = @"Four score and seven years ago


our founding fathers
brought forth on this continent...";

The results can be seen with: MessageBox.Show(strlongString);:

ASCII Codes:

All typed characters are represented with an ASCII code. For example, with the English
code-set, the letter 'A' is represented with an ASCII 65 while a lower-case 'a' is ASCII 97.
The code can also show other, non-visible characters. A tab is ASCII 9 and
CRLF (\r\n) is ASCII 13 + ASCII 10.

Chapter 4 - String Manipulation Page: 169


ASCII is still widely used by programmers even though, with Windows XP and above, it
has been replaced with a more-capable, language-neutral, "Unicode" standard. Working
with ASCII is somewhat esoteric and is mentioned here as reference.

This code demonstrates how to convert from 'A' to the number ASCII 65 and back. Note
the new type of declarations, "char" and "byte".

Example: Character to ASCII; ASCII to String


private void button1_Click(object sender, EventArgs e)
{
//Convert from a single-Character value to an ASCII code:
char myChar = 'A';
byte myByte = (byte) myChar;
int myInt = (int) myByte;

MessageBox.Show(myInt.ToString());

//Convert from a numeric ASCII code to a string:


string myString = Convert.ToChar(65).ToString(); //A
MessageBox.Show(myString);
}

Chapter 4 - String Manipulation Page: 170


String Concatenation

String concatenation combines two or more separate strings into one. C# uses the "+"
(plus symbol) for string concatenation but Visual Basic and Excel programmers will
remember the "&" symbol. Previous chapters have used this idea several times.

For example, concatenate two strings into one result:

Example: String Concatenation, complete


private void button1_Click (object sender, EventArgs e)
{
string strtextA = "Cat";
string strtextB = "Dog";

MessageBox.Show(strtextA + strtextB);
MessageBox.Show(strtextA + " and " + strtextB);
}

Results: "CatDog", "Cat and Dog". Note the second example uses the word " and " with
leading and trailing spaces.

string.Concat ( ) and "+":

An alternate way of concatenating uses the string.Concat method and this is identical
to the "+" method above. There is no limit to the number of strings that can be
concatenated:

MessageBox.Show(string.Concat(strtextA, strtextB, strtextC))

or

MessageBox.Show(strtextA + strtextB + strtextC)

In C#, the "+" symbol actually translates to the 'string.Concat' method. Since this
happens at compile time, there is no particular benefit in choosing one method over the
other. Most developers use the plus.

comments about string.Concat( )

".Concat( )" is a "method" applied against "string." You can think of a method as a
subroutine or function and methods always require parenthesis. Some methods, such as
the .Concat, require values to be passed through the parenthesis, while others can be
empty, as in .ToString( ) or .ToUpper( ). There is more on this as the chapters progress.

Chapter 4 - String Manipulation Page: 171


Numeric Values and Strings:

Both string.Concat and "+" can append numeric values (as a string) to other strings,
without first having to explicitly convert the numbers to a string (This is a change,
starting in Visual Studio 2005/2010). For example, the following appends the letter "N"
and a numeric integer into one string, forming "N1030". The code example shows three
different techniques to do this, all yielding the same results:

Example: Convert.ToString( ), complete


private void button1_Click (object sender, EventArgs e)
{
//Each variation produces the same results: "N1030"

string strpartNumberPrefix = "N";


int iPartNumber = 1030;
string strassembledPartNumber;

strassembledPartNumber =
string.Concat(strpartNumberPrefix, iPartNumber);
MessageBox.Show (strassembledPartNumber);

strassembledPartNumber =
strpartNumberPrefix + Convert.ToString(iPartNumber);
MessageBox.Show (strassembledPartNumber);

//New in VS2008 and above:


strassembledPartNumber = strpartNumberPrefix + iPartNumber;
MessageBox.Show (strassembledPartNumber);
}

Results: Each results in "N1030".

Each of the examples are acceptable but the "Convert.ToString" most clearly indicates
your intentions. It is somewhat surprising that C# allows an implicit conversion from
numeric to string, especially in the third example. Prior versions of C# did not allow
this, especially if the first term were numeric, and out of habit, I still tend to use
Convert.ToString.

Starting in VS2012, the first parameter in a MessageBox statement can also be numeric
and it will be implicitly converted to a string.

Concatenating Floating Point Numbers:

Floating-point numbers (non-whole, numeric values with decimals, also known as


"Double" and "Single") can be concatenated to a string. Consider the following
example. Notice how floating-point numbers are declared and initialized (with an "F");
more on this in the next chapter:

string strpartNumberPrefix = "N";


float floatingPartNumber = 123.456F;

MessageBox.Show(strpartNumberPrefix + floatingPartNumber);
MessageBox.Show

Chapter 4 - String Manipulation Page: 172


(strpartNumberPrefix + Convert.ToString(floatingPartNumber));

Results: "N123.456"

In real life, no programmer would define a "part-number" as a


floating-point number, even if it has a decimal point. This
should really be declared as a string. Only declare numeric
values if you intend to perform mathematical operations.

Chapter 4 - String Manipulation Page: 173


String Comparisons, revisited

The if-statements in the prior chapter introduced the concept of equalities. Numeric
values were easily compared with double-equal-signs (==) and with greater-than and less-
than symbols (<, >). But string comparisons are more complicated. Keep these rules in
mind when comparing strings:

• All string-comparisons are case-sensitive, where upper and lowercase matters.


• You cannot directly test if a string is "less than" another string (e.g. "Abe" <
"Zebra").
• Use String.Compare (string1, string2) to compare strings.

Comparisons are Case Sensitive:

This example compares two similar part numbers, one with an uppercase prefix and the
second with a lowercase prefix - "N1030" and "n1030":

String: Equal Comparisons, no match because of case


private void button1_Click (object sender, EventArgs e)
{
//Mixed-case strings do not compare equal:

string strtextA = "N1030";


string strtextB = "n1030";

if (strtextA == strtextB)
MessageBox.Show("they are equal");
else
MessageBox.Show("they ain't equal");
}

Results: "they ain't equal"

Case-Insensitive Comparisons:

To compare two strings, regardless of case, use " .ToUpper()" or ".ToLower()".


Consider these flawed comparisons:

if (strtextA == strtextB.ToUpper())
if ("N1030" == "n1030".ToUpper())

These are unsafe tests because although strtextB was shifted to uppercase and
successfully tested, what would happen if strtextA ("N1030") were accidentally set to
lowercase by another routine? This is easily fixed by shifting both sides to upper-case,
in this time-honored tradition:

if (strtextA.ToUpper() == strtextB.ToUpper())

Chapter 4 - String Manipulation Page: 174


.ToUpper() and .ToLower() are string methods and all
methods require parenthesis, even if there is nothing to pass into
the function. See later in this chapter for more details.

string.Compare( ):

It will be a shock to VB Programmers, but C# does not allow ">" and "<" symbols in a
string comparison. This forces you to use a more cumbersome "string.Compare" method
for inequalities but it does have a minor improvement because you can make the
comparisons case-insensitive.

The syntax uses a lower-cased keyword "string" (type the word, 'string') followed by a
dot-Compare. Within the parenthesis, list the two variables to compare. A third
parameter specifies whether the comparison is case-sensitive or not (bool ignoreCase).
Oddly, string.Compare( ) returns an integer -1, 0, or 1, instead of a True or False:

integerResult =
string.Compare (string1, string2, IgnoreCase:True|False)

Example: string.Compare with inequalities (<, >, =), complete


private void button1_Click (object sender, EventArgs e)
{
string strtextA = "Abe";
string strtextB = "Zebra";
int iresult;

iresult = string.Compare(strtextA, strtextB, true)

//where integer result:


// +1 = A > B
// 0 = A = B
// -1 = A < B
// ",true" = case-insensitive; ",false" = case sensitive

MessageBox.Show("Results: " + Convert.ToString(iresult));

//Testing should be done with a switch statement,


//discussed shortly...
switch(iresult)
{
case -1:
MessageBox.Show(strtextA + " < " + strtextB);
break;
case 0:
MessageBox.Show(strextA + " = " + strtextB);
break;
case +1:
MessageBox.Show(strtextA + " > " + strtextB);
break;
}
}

Chapter 4 - String Manipulation Page: 175


where:

• string.Compare() uses the actual word "string" - you are not replacing this with
your variable names until inside of the parentheses. This command reads as "The
'Compare' method of the 'string' Class.

• string.Compare returns an integer, -1, 0, 1, and in this example, it is being written to


an explicit variable:

0 if equal
+1 if textA comes alphabetically before textB
-1 if textB comes alphabetically before textA

• The third parameter, a boolean "IgnoreCase: True|False", is optional but is almost


always set to "true". Use "True" for case-insensitive tests, use "False" when you
want an exact case-match.

Using string.Compare for Equality:

With the IgnoreCase flag, string.Compare can test if two strings are equal, saving the
trouble of shifting both strings to upper or lowercase before the test:

Example: string.Compare, case-insensitive, complete


private void button1_Click (object sender, EventArgs e)
{
string strtextA = "N1030";
string strtextB = "n1030";

if (string.Compare (strtextA, strtextB, true) == 0)


{
MessageBox.Show("they are equal");
}
else
{
MessageBox.Show("they ain't equal");
}
}

As you type "string.Compare", the editor's intellisense displays a pop-up help-box with a
short description of the command, along with a list of available parameters. Note the
"int" (integer) term on the first line, acting as a reminder on what is returned by the
function.

Chapter 4 - String Manipulation Page: 176


The results of the string.Compare can be tested with a series of nested-ifs or better with a
switch statement:

Testing a string.Compare with nested-if, completed but not recommended


private void button1_Click (object sender, EventArgs e)
{
//Testing a string-compare using a nested-if:
//Remember: string.Compare returns a -1, 0, 1
//(Recommend using a switch-statement instead)

string string1 = "Abe";


string string2 = "Wilcox";

int compareResults = string.Compare (string1, string2, true);

if (compareResults == 0)
MessageBox.Show ("Both values are the same");
else
{
if (compareResults == +1)
MessageBox.Show ("Value1 is first alphabetically");
else
MessageBox.Show ("Value2 is first alphabetically");
}
}

where:

• The compare-results (-1, 0, 1) is stored in an intermediate variable near the top of the
routine. This keeps you from having to perform the test a second-time within the
nested-if.

• As a reminder:
+1 string1 > string2 (alphabetically, string1 is first)
0 string1 and string2 are equal
-1 string 2 > than string1

• In the example, string1 is alphabetically first. The variable order makes a difference
in the results from the string.Compare.

• Nested-ifs are cumbersome. A switch-statement is a better way to code this; see the
next section.

Testing string.Compare using a switch Statement:

Using if-statements to test for a three-way string.Compare (0, +1, -1) is cumbersome.
Because of this, most programmers use a "switch" statement (see previous chapter).

Chapter 4 - String Manipulation Page: 177


Comparing multiple conditions with a "switch", recommended

The order of the case-tests do not matter; the first true condition is used. Notice a
declared integer holding-value for the compare-results is not needed; contrast this with
the nested-if example, which needed an integer iresult.

where:

• "string.Compare" begins with a lower-case keyword 'string" followed by a dot-upper-


case '.Compare'. The two stringed variables being compared are listed within the
parenthesis, comma delimited.

• The third parameter ("IgnoreCase": true|false) controls the case-sensitivity of the


command. "true" means the comparison is case-insensitive (where caT == CAT)
and this is how the function is almost always used. You must explicitly use the 'true'
switch to enable this feature.

• When inserting "string.Compare" into an "if" or "switch", keep in mind that the (if-
statement) needs its own parenthesis - which are separate from the parens used by
the string.Compare. Notice the two leading and closing parenthesis:

switch (string.Compare(strtextA, strtextB, true))

Chapter 4 - String Manipulation Page: 178


string.Equals():

"string.Equals" performs the same test as "==", returning a true|false. The test is strictly
case-sensitive and does not perform greater-than or less-than comparisons. It is
mentioned here because some programmers believe this command has a slight
performance gain over a simple ==; however, the compiler translates all string double-
equals to this function at compile time, so the gains are artificial. Regardless, this
function is self-documenting and syntactically consistent with other string functions.

string.Equals( ), complete
private void button1_Click (object sender, EventArgs e)
{
string strtextA = "n1030"
string strtextB = "N1030"

//string.Equals returns a true/false:

if (string.Equals (strtextA, strtextB))


{
// <stuff to do if equals>
}
}

Results: <stuff to do if not equal>. This is only a case-sensitive compare, where "n" and
"N" are not equal.

string.CompareOrdinal:

This is a seldom, if ever-used comparison and is here for


reference.

string.Compare( ), alphabetically compares two strings, using a 'dictionary-based'


comparison where "a" comes before "A". But internally computers represent data using
numeric ASCII codes (discussed earlier in this chapter). ASCII sorts in a numeric order
that is alphabetic within the group of lower and upper-cased letters, but not alphabetic
with mixed case.

Chapter 4 - String Manipulation Page: 179


For example, the letter capital 'A' is internally stored as an ASCII 65 while a lower-case
'a' is stored as ASCII 97. Here is a partial list of some of the characters represented by
the ASCII tables. Full ASCII charts can easily be found on the Internet.

Char ASCII Char ASCII Char ASCII

A 65 a 97 0 48

B 66 b 98 1 49

C 67 c 99 2 50

D 68 d 100 3 51

Z 90 z 122 9 57

The computer needs a way to track both visible and non-visible "characters," and this is
why DOS used an 128/256 character ASCII table. (Other computer systems use different
schemes - for example, IBM mainframes use an encoding called "EBCIDC" (or Hex).
Windows now uses a newer table called "Unicode" - which represents 65,000 characters
– giving the ability to display Asian languages.)

You can probably see where this is leading. string.CompareOrdinal compares two text
strings using the ASCII table for the sort-order. For example, "A" comes before "a"
(ASCII 65 < ASCII 97).

Use string.CompareOrdinal in the same manner as string.Compare with only one


exception: this command is not case-sensitive so there is not a 'true|false' switch.

string.CompareOrdinal( ), complete
private void button1_Click (object sender, EventArgs e)
{
//string.CompareOrdinal compares ASCII values,
//not by a Dictionary Compare

//Returns and integer where:


// 1 = A > B
// 0 = A = B
//-1 = A < B

int result;
iresult = string.CompareOrdinal(strtextA, strtextB)

MessageBox.Show("Results: " + Convert.ToString(iresult));


}

where:

result = integer 0 if equal


result = integer -1 if ASCII Code textA is before textB
result = integer -1 if ASCII Code textB is before textA

Chapter 4 - String Manipulation Page: 180


<string>.EndsWith

This method examines the end of a string, looking for a passed-value. If found, the
function returns a boolean true/false. A common use is to look at a filename's extension.
Typically, the command is paired with an if-statement.

General use:
<original-string>.EndsWith(<compare-string>)
<original-string>.EndsWith(<compare-string>, true, null)

where:

• <original-string> is the string you are comparing, e.g. "myfilename.xlsx"


• <compare-string> is the string you are looking for. For example, ".xlsx", "Jr."
• Parameter-2: "true" makes the comparison case-insensitive
• Parameter-3: "null" use current culture (English); and is required if using parm-2

Detecting .XLS file Extensions:

Consider this test to see if the filename is likely an Excel spreadsheet.

A simplistic test like this would likely fail because of upper and lowercase concerns (.xls
vs .XLS, plus a concern with Excel's newer style, .xlsx):

if (myFileName.EndsWith(".xls"))
{
//Do this stuff
}

Either of these is a better test because both are case-insensitive:

if (myFileName.ToLower().EndsWith(".xls"))
if (myFileName.EndsWith(".xls", true, null))

The next example carries the test to a more capable level, detecting both the traditional
Excel xls extension and the newer .xlsx, introduced with Microsoft Office 2007. For
efficiency, the test converts the filename to lowercase one time, rather than with each
sub-statement:

Chapter 4 - String Manipulation Page: 181


Example: EndsWith: Detecting Excel File Extensions, complete
private void button1_Click (object sender, EventArgs e)
{
//Detect if the filename is an Excel sheet or not;
//supports Office 2007's new file extensions, which made this
//test a little more complicated.

string myFileName = "C:\\data\\09Financials.xls";

myFileName = myFileName.ToLower();

if (myFileName.EndsWith(".xls") ||
myFileName.EndsWith (".xlsx") ||
myFileName.EndsWith (".xlsm") ||
myFileName.EndsWith (".xlsb"))
{
MessageBox.Show("This is an Excel File");
}
else
MessageBox.Show("This is not likely an Excel File");
}

Wild-cards cannot be used.

Example: Appending a Closing Backslash:

This example uses .EndsWith to help standardize end-user-typed path names, forcing a
closing backslash, preparing for a fully-qualified path (See also Chapter 23:
Path.Combine). For example, if the user typed: "C:\Data", change it to "C:\Data\". If
they typed "C:\Data\" or if they typed a complete path and filename, such as
"C:\Data\09Financials.xls", leave it as-is.

Chapter 4 - String Manipulation Page: 182


Eample: EndsWith - Appending Backslash to path, completed
private void button1_Click (object sender, EventArgs e)
{
//Example program that appends a trailing backslash to a path
//name if the slash doesn't already exist.
//e.g. C:\Data becomes C:\Data\

string strpassedUserPath = "C:\\Data";

if (strpassedUserPath.EndsWith(".xls", true, null))


{
// an entire xls filename was passed; leave as is...
}
else
if (strpassedUserPath.EndsWith("\\"))
{
// user typed a closing backslash; leave as-is; no append
}
else
{
// no closing backslash on path; help them out: append
strpassedUserPath = passedUserPath + "\\";
}
}

MessageBox.Show("Final User Path is: " + strpassedUserPath);


}

where:

• Note the strpassedUserPath = "C:\\Data" uses two backslashes to represent a single


backslash. Backslash is an escape character, as described at the top of this chapter.

• If a closing backslash is not found, append one with a simple concatenation.

• Presumably, another routine would append a filename to the user-path.

Note: This example is flawed in that it only considers ".xls" files but still demonstrates
the concept. A more substantial routine could be written by looking for a period
(extension).

Chapter 4 - String Manipulation Page: 183


<string>.Length

A string's numeric length is found by examining its ".Length" property. Lengths are
often needed in substring calculations and in data-entry validations. This example looks
at text typed in textBox1:

<string>.Length, complete
private void button1_Click (object sender, EventArgs e)
{
//Display the length of a typed-string in textBox1

int ipartNumberLength;

ipartNumberLength = textBox1.Text.Length;

MessageBox.Show
("The number of characters in TextBox1 is: " +
Convert.ToString(ipartNumberLength));
}

where:

• ".Length" is a property (not a method or function). Because of this, it does not have
parenthesis. The Length property can not be changed in code. See below for
information on how to identify a property from a method..

• Lengths are base-1 counters, where the first character is at position 1, the second at
position 2, etc. In C#, all lengths are base-1 while all other commands start counting
at base-zero, where 0 is the first position.

• If present, leading and trailing spaces are counted as part of the length.

• Empty strings ("" quote-quote) have zero lengths. Note this is not an undefined or
null length - the length is actually zero.

• textBox fields default as empty-strings and as such have lengths = 0.

• Null strings have undefined lengths. If you try to retrieve a null-length, the program
will abend and logic is needed to trap the event. Detecting empty and null strings
takes special care; see later in this chapter for details, and again in Chapter 6.

• Numeric variables do not have lengths and the program will not compile if
attempted.

Identifying Properties:

All functions and methods use (parenthesis) but properties such as .Length do not. The
editor gives a hint about which-is-which while typing. For example, as you type

Chapter 4 - String Manipulation Page: 184


"textBox1.Text" and press "period (for -Length)", the editor displays a list of available
functions and properties. Notice the .Length popup icon is a black wrench (a property)
while methods are boxes.

Hovering over the "Length" context menu displays additional fly-out help. The first
word shows "int" (the results of the property is an integer value) and it expects a string as
input (textBox1.Length). Now that all this has been explained, standard strings, such as
strtextA, have only one property – Length; but other objects, such as textBoxes, have
nearly a hundred properties, including Width, Background Color, base-fonts, border-
styles and the like. More about this later.

Chapter 4 - String Manipulation Page: 185


<string>.ToUpper( ), .ToLower( )

<string>.ToUpper( )
<string>.ToLower( )

.ToUpper( ) and .ToLower( ) convert strings to upper and lowercase.

strmyName = textBox1.Text.ToUpper();
strsomeValue = strsomeValue.ToLower();

Because this is a method (note the box-icon in the illustration below), parenthesis are
required. But in this case, the method does not accept parameters and the parenthesis are
always empty.

Strings and textBoxes are often shifted for if-statement comparisons and the shift
happens temporarily, just for the test. For example, the next code-block compares what
was typed in textBox1 with a hard-coded string "MY DEAR AUNT SALLY". Users can
type "my dear aunt sally" in any case: upper, lower or mixed, and the if-statement's shift-
to-upper will generate a positive match.

<string>.ToUpper( ) / <string>.ToLower( ); If-statement comparison


private void button1_Click (object sender, EventArgs e)
{
//Convert a string .ToUpper case (also available: .ToLower)
//Assuming textBox1 has this value typed in the field:
//"My Dear Aunt Sally";

if (textBox1.Text.ToUpper() == "MY DEAR AUNT SALLY")


MessageBox.Show ("This must be Sally: " + textBox1.Text);
else
MessageBox.Show("Someone else: " + textBox1.Text);

The if-statement's shift was only for the duration of the test because the .ToUpper was
not assigned to a new value. In other words, textBox.Text remains mixed-case once the
if-statement completes.

To permanently shift to uppercase, assign the variable to itself, as in:

textBox1.Text = textBox1.Text.ToUpper(); //Notice the dot-Text

Chapter 4 - String Manipulation Page: 186


As a reminder, in C# assignments are marked with an equal-sign, as in 'a value "gets"
another value.' Meanwhile, if-statements use double-equal-signs, indicating a
comparison.

Other string variables (not just textBoxes) can be shifted similarly:

strtestA = strtestA.ToUpper();

comments:

• Not much is accomplished if you have this line of code, by itself:


strtestA.ToUpper();

Since it was not assigned to a variable, the compiler shifts then discards the
uppercase results. Instead, use this command:
strtestA = strtestA.ToUpper();

• All Methods have parenthesis. This includes loops, if-statements, Concats, and
.ToUpper and .ToLower. Parenthesis are required even if no values are passed
through them. This is described in more detail in future chapters, but for now,
remember methods always have parenthesis while "properties," such as ".Length",
do not.

• If you forget the parenthesis after a .ToUpper or .ToLower, you'll be blessed with a
compiler error similar to this:

VS2012 - VS2015:
Cannot convert method group 'ToUpper' to non-delegate type 'int'. Did you intend
to invoke the method?

VS 2010 and older:


Operator "==" cannot be applied to operands of type 'method group' and 'string'

• With textboxes, it is easy to forget the .Text property. In other words, you cannot
assign a string to the object, you must assign it to the property:

textBox1 = textBox1.ToUpper(); //Incorrect


textBox1.Text = textBox1.ToUpper(); //Incorrect

textBox1.Text = textBox1.Text.ToUpper(); //Correct

As a reminder, properties appear this way in the Intellisense editor. You will also
see methods (such as ToUpper) or events (such as "Click" event). Events are
discussed in future chapters.

Chapter 4 - String Manipulation Page: 187


Exercise I: "ToUpper":

The .ToUpper() and .ToLower() methods work on any textual data – including
textBoxes, standard variables. Create a new program or modify an existing, giving it the
following characteristics: A single button, "button1" and two textBoxes, textBox1 and
textBox2.

When button1 is clicked, take a hard-coded string ("Space Ghost") and shift to
uppercase, storing the results in textBox1. Then store a lowercase result in textBox2.

A Reminder:

See Chapter 1 for details on how to build the initial program. Click in "Form1.cs
[Design]" to get to the form design view. Use the Toolbox flyout to populate the panel.
with the three objects. Attempt the program now before reading the steps.

Program 4.1: String Manipulation


Detailed Steps:

1. Build a new project or continue modifying the previous examples.

Chapter 4 - String Manipulation Page: 188


2. Open Form1.cs in design view by double-clicking "Form1.cs" in the Solution Explorer or
by pressing Shift-F7.

Then, from the Toobox flyout menu, "Common Controls", click and drag two "textBox"
objects from the menu to the panel. Drag a "button" object. Arrange them so they look
like the illustration above. If loading for the first time, the toolbox flyout may take a
moment to load.

Click each textbox and examine its properties (Name) to decide which is named
textBox1 and textBox2.

3. Double-click button1, taking you to button1's "Click event." (More on events in the next
chapters.) Add this code:

Example: ToUpper( ) and .ToLower( )


private void button1_Click(object sender, EventArgs e)
{
strtestString = "Space Ghost"; //A hard-coded value

//Convert the hard-coded string to upper and store in textBox1


textBox1.Text = strtestString.ToUpper();

//Convert the hard-coded string to lower and store in textBox2


textBox2.Text = strtestString.ToLower();

//Show the original hard-coded string was not changed by the


//manipulations:
MessageBox.Show
("strtestString is still equal to mixed: " + strtestString);
}

4. Press F5 to run the program; then click button1.

Results when button1 is clicked: textBox1 gets an upper-cased "SPACE GHOST" and
textBox2 gets a lowercased string. The subsequent MessageBox shows testString
remains unmodified (case). Notice the use of "textBox1.Text" – you must use the .Text
property.

Program 4.2: textBox1 ToUpper, directly

Chapter 4 - String Manipulation Page: 189


<string>.PadLeft()

<string>.PadLeft (int);
<string>.PadRight (int);
<string>.PadLeft (int, '*');

".PadLeft" and ".PadRight" append spaces (or other characters) to the string, bringing the
total length to the value of the passed integer. Left-padding with spaces can help to align
lists or columns of numbers, especially in printed reports.

This example takes two numbers and pads them left, for a total length of 10. The results
are displayed both on the panel and in a MessageBox. The panel will be set to a fixed-
space font while the MessageBox will remain a proportional font.

See also Chapter 21 Formatting for other alignment options.

Example Setup:

A. In Design View, change textBox1 to a multi-lined textBox.


Highlight the field with a single-click.
Click the little-black arrow in the upper right, marking "multi-line".
Drag the box's handles, making it larger, as illustrated.
If needed, delete textBox2 from previous examples.

Note: If, from previous examples, you have a textBox2, delete or move it down, making
room for the multi-lined textbox.

B. Highlight textBox1. In the Properties Window (lower, Right corner of the design view
window), change the font to Courier-New:

Chapter 4 - String Manipulation Page: 190


C. In Code View (double-click "button1"), modify the event with these new statements:

Example: .PadLeft( ), complete


private void button1_Click (object sender, EventArgs e)
{
//Pad a string on the left side with spaces
//and display the results in textBox1

string strString1 = "1000.00";


string strString2 = "99.00";

strString1 = strString1.PadLeft(10); //appends space by dflt


strString2 = strString2.PadLeft(10, ' '); //Explicit; note tics

//Show the results in textBox1 (on the panel)


//Assumes this is a multi-line textBox and courier-new font
textBox1.Text = strString1 + "\r\n" +
strString2;

//Show the results in a messageBox with a proportional font


//and embedded tic-marks
MessageBox.Show ("'" + strString1 + "\r\n" +
"'" + strString2);
}

Chapter 4 - String Manipulation Page: 191


Results: Both the textBox and the MessageBox were padded with 10 characters but the
view is different in the MessageBox because of the proportional-spaced font. The textBox
looks great. Typically padding is done for printed reports using fixed-space fonts.

where:

• Padding strString1.PadLeft(10) assumes a space as the pad-character.

• Padding strString2.PadLeft(10, ' ') explicitly used a space-character. Note


the tic-marks, which indicates "character data" – characters are single-position strings
and are delineated with apostrophes. More on this below.

• The length of the passed integer (e.g. 10) is expected to be longer than the starting
string's length or nothing appends. In other words, padding with 5 has no affect when
the length of the "1000.00" is already 7 long.

• Generally, when trying to align a stack of numbers, pick a length that is 3+ the widest
expected (string) number. This accounts for a two-character-wide column separator
plus room for a +/- sign.

• When padding, the output device almost certainly needs to be a fixed-width, non-
proportional font. Recommend the true-type font "courier new".

Chapter 21 describes how to format numbers with decimals,


commas, and other alignment issues. For simplicity, this chapter
looks only at string data.

Chapter 4 - String Manipulation Page: 192


Debugging Embedded Spaces with tics:

Since spaces are hard to see, the example appended a tic-mark (apostrophe) before and
after the MessageBox's testString. Notice the quote-tic-quote. The resulting punctuation,
(tic) ' 1000.00', shows where the spaces are. This is a trick and is only useful while
debugging with MessageBoxes.

<string>.PadLeft(int, '*');

.PadLeft is not restricted to a space as a fill-character, however, only single-characters are


allowed and they are always delimited with tic-marks – not quotes. For example, in
.PadLeft(int, '*');, the '*' (or any other character) is a new data-type called "char"
with more discussions about this in the .Trim section. Bank checks and the like, are often
printed with leading asterisks.

In this block of code, display a numeric value with a leading dollar-sign and asterisks.
The number is a floating-point number (not an integer or a string) and it is declared using
an "F". More on numbers in the next chapter:

Example: .PadLeft( ) with leading characters, complete


private void button1_Click (object sender, EventArgs e)
{
//Print a floating point number with leading dollar-sign and
//asterisks:
// ***$1800.99
//Note the parenthesis near the dollar sign; see text

float fDollarAmount = 1800.99F;

string strPrintedString =
("$" + fDollarAmount.ToString()).PadLeft(10, '*');

MessageBox.Show(strPrintedString);
}

Chapter 4 - String Manipulation Page: 193


where:

• Because of the parenthesis, the dollar-sign is tucked up against the number


(***$1800.99) rather than ($***1800.99). Notice how this is treated as one item
because of the parenthesis:

("$" + fDollarAmount.ToString())

Then these results are padded with asterisks.

• The same effect could be achieved in this fashion, reading left-to-right. But I like the
first method because the order is explicit:

"$" + fDollarAmount.ToString().PadLeft(10, '*')

In this example, hardcoding 10 characters for padding will cause problems for numbers
longer than 10 characters. More advanced number formats, discussed in future chapters,
need to account for decimals, commas and other punctuation. A better solution is to print
at least 3 leading "stars," regardless of the length. "Variablize" this by incorporating a
length calculation into the passed integer. Take the length of the existing string and add
three + 1 for the dollar-sign.

string strPrintedString =
("$" + fDollarAmount.ToString())
.PadLeft(fDollarAmount.ToString().Length + 4, '*');

See also Chapter 21, Formatting, for more information.

Date Padding with Leading Zeros:

Padding with leading zeros is often needed with day and dates, changing a single-digit
month like "5" to "05". Here is a typical example.

The Formatting chapter has a more graceful way to pad numeric


dates (using MM, DD).

Chapter 4 - String Manipulation Page: 194


Example: PadLeft on Month, changing '5' to '05', complete
private void button1_Click (object sender, EventArgs e)
{
//Append a leading zero on single-digit dates

string strtestMonth = "5";


MessageBox.Show ("'" + strtestMonth.PadLeft (2, '0') + "'");
}

Results: a single-digit "5" becomes "05". A double-digit,"12", remains unchanged


because it was already at the minimum length.

Chapter 4 - String Manipulation Page: 195


<string>.Trim( );

The .Trim method removes leading and trailing spaces from a text string. User-typed
data-entry fields always should be trimmed because users will type leading and trailing
spaces for reasons unknown. When parsing input text files, especially if the input files
were generated by human beings, you invariably need to trim the data.

For example, the text string " Space Ghost", with leading spaces, is trimmed to "Space
Ghost" when passed through the function. Often the result of the trim is assigned directly
back to the field being trimmed:

Example: <string>.Trim( ), complete


private void button1_Click (object sender, EventArgs e)
{
//Trim leading and trailing spaces from a test string
//or from a populated textBox:

strtestString = " Space Ghost ";

strtestString = testString.Trim() //standard trim


textBox2.Text = textBox2.Text.Trim() //trim a textBox
}

where:

• An unadorned .Trim method still requires a set of open-and-close parenthesis. By


default, this trims leading and trailing spaces. In a later section, other trim-characters
can be used.

C# does not trim redundant internal (embedded) spaces. For example, "Space Ghost",
with 3 internal spaces, remains the same once trimmed. This will require a specialized
function; see Chapter 8 for a SuperTrim function.

Combining Commands:

Often, the dot-trim method is combined with other commands. For example, you could
trim leading spaces, shift to upper-case and then pad with leading asterisks, all in one
command. The results of the statement are often assigned back on top of the original
variable. The command is read and processed from left to right:

Chapter 4 - String Manipulation Page: 196


Example: Combined Trim, ToUpper and Pad, complete
private void button1_Click (object sender, EventArgs e)
{
//Trim, Uppercase and append leading asterisks with
//one statement:

string strtestString = " Space Ghost";

strtestString = strtestString.Trim().ToUpper().PadLeft(14, '*')


MessageBox.Show (strtestString);
}

Results: "***SPACE GHOST"

<string>.TrimStart()
<string>.TrimEnd()

.TrimStart() trims leading spaces, leaving internal and trailing spaces, and .TrimEnd()
removes from the end. (Visual Basic used "LTrim" and "RTrim".) This example removes
leading spaces and uses tic-marks to show the results:

Example: .TrimStart( ) / .TrimEnd( ), complete


private void button1_Click (object sender, EventArgs e)
{
//Trim only leading spaces

string strtestString = " Space Ghost ";

MessageBox.Show ("'" + strtestString.TrimStart() + "'");


}

Results in 'Space Ghost '. (Trailing spaces survived; printed with tic-marks).

Trimming with other Characters:

By default, Trim removes leading and trailing spaces but the command can be modified to
trim other characters. For example, you could trim leading asterisks or quotes.

As neat as this sounds, this is less-than-useful because if you knew the characters you
needed to remove, you probably also know how many and where they were. The added
setup time isn't worth the trouble and other methods could be used to remove the text.
However, since you might occasionally see this command it is still worthy of discussing
and it introduces new concepts.

As an aside, the additional features of the Trim methods are


called "overloads" – this is when a command's capabilities are

Chapter 4 - String Manipulation Page: 197


extended with new features and abilities. You'll learn more about
this when you start writing your own methods in Chapter 6.

char Data-Types Explained:

In order to Trim with other characters, two new concepts are introduced: The 'char'
variable and "arrays."

A "char" (character) data-type is a single-character string. From the compiler's view, a


'char' data type takes a tiny amount of memory (one byte) and it is fast and efficient. To
aid the compiler, char data-types are always delimited with 'single-tics' (apostrophes),
instead of "double-quotes." For example, compare these two declaration statements:

string strtextString = "a" // This is a string


char charCharacter = 'a'; // This is not a string; Note the
tic-marks; no quotes

Earlier, Trim methods always end with a pair of empty parenthesis, as in:
strtestString.Trim();

However, inside the parenthesis you can pass 'char' data. For example, to remove the
letter 'C' from a prefixed-part number ("C1030"), use either of the two following
commands, where the second is more reliable:

Example: .Trim( 'characters') / .TrimStart ('c')


private void button1_Click (object sender, EventArgs e)
{
string strpartNumber = "C1030";
string strstrippedPartNumber;

//Method 1
//(Will fail if leading spaces are present)
//(Will fail if the 'c' is lower-cased)

strstrippedPartNumber = strpartNumber.Trim('C');
MessageBox.Show(strstrippedPartNumber);

//Method 2: Trims only from the front


//(Will fail if the original string has a leading space
//but works (for lower and uppercased partNumbers)

//for example: " C1030". Read further.

strstrippedPartNumber = strpartNumber.ToUpper().TrimStart('C');
MessageBox.Show(strstrippedPartNumber);
}

There are un-intended consequences with the two Trim statements shown above. The
first command removes both leading and trailing 'C's. If the part-numbers happened to to
be "C1030C", the 'C' is trimmed from both sides.

The second command resolves this problem by trimming only the leading character using
"TrimStart" and it also fixes the problem when you don't know if the part-number is upper

Chapter 4 - String Manipulation Page: 198


or lower cased. The entire string is shifted before trimmed with a capital-C. The order of
the commands is important; shift before Trimming. The following Trim statement, while
syntactically correct, would not work because the operations are in the wrong order:

strpartNumber = "c1030" // note lower-cased

strstrippedPartNumber =
strpartNumber.TrimStart('C').ToUpper(); //fails

Results: non-strippedPartNumber = "C1030" with the capital 'C' un-trimmed. Uppercase


first, then trim. If the user typed " c1030" (leading spaces), the command would still fail.

char-Arrays and Trim Statements:

In another contrived example, what if your part-numbers could have different prefixes,
such as "A", "B", "C" and possibly leading spaces and you needed to trim the prefixes?
You could use four separate Trim statements or you could use an extended feature,
passing an 'array' of individual characters for the trim. In one statement, all of these
possible prefixes could be trimmed.

An array is a list of similar, individual items. Each item stands


independent of the others, but they are contained in a common
group. (More details about arrays are found in later chapters,
but for now, the Trim command will introduce the topic.)

Changes in VS2012:

Starting in Visual Studio 2012, Trimming with multiple characters was blessedly
simplified. An array of valid trim-characters could be passed, via an explicit array, or
now you can trim using an implicit array, where the values are typed directly into the
statement. The new design is easier to code and is easier to explain.

To trim the characters 'A', 'B', 'C', and ' ' (space) from a part-number prefix, use an
extended Trim command (called an "overload").

Trim with an Implicit Array (VS2014, 2015), complete


private void button1_Click(object sender, EventArgs e)
{
//Trim selected prefixes from a part-number

string strpartNumber = " B1030";

strpartNumber = strpartNumber.Trim('A', 'B', 'C', ' ');

MessageBox.Show("New Trimmed PartNumber: " + strpartNumber);


}

Results: "1030" with the prefixes and leading and trailing spaces removed.

Chapter 4 - String Manipulation Page: 199


Notice you cannot use this syntax: .Trim("abc "). The Trim command insists on
'character' data and this bad example is using a "string."

Visual Studio 2010 and Older:

With VS2010 and older, you had to build an explicit array to pass into the Trim function.
Even with newer versions, it is still useful to learn this technique – especially if the
number of Trim characters are in a longer list, where the values are more easily
controlled.

Begin by building a character array. Declare a "char" variable, much like you would
declare a string, but after the char-data-type add brackets, where the brackets indicate an
array. Unlike Visual Basic, the brackets go after the data-type (rather than the variable
name).

//Declare the array...


char[] <then the name of the array>;

The variable's name can be any invented name and these examples I am using a long and
descriptive name, "aremoveTheseCharacters", where I like to use a prefix "a", reminding
me this is an array. Within the array, specify each individual (part-number prefix), using
character-tic-marks and comma-separating the values:

char[] aremoveTheseCharacters = {'A', 'B', 'C', ' '};

You may have correctly surmised there can be different types of arrays: string arrays,
numeric arrays, and 'char' arrays. However, Trim only accepts 'char' arrays.

char[] aremoveTheseCharacters = {'A', 'B', 'C', ' '};

The braces mark the beginning and ending of the list and this list becomes the array.
Notice how the array includes a space-character, which is an apostrophe-space-
apostrophe. There are other ways to load this array and this is left for Chapter 22, Arrays.

Chapter 4 - String Manipulation Page: 200


Feeding the Array into the Trim:

The array will be fed into the .Trim command and it will trim the letters A, B and C, as
well as spaces. Place the array's name (aremoveTheseCharacters) in the Trim's open-and-
close parenthesis.

Trimming other characters using an array, complete


private void button1_Click (object sender, EventArgs e)
{
//Declare a character array and trim various prefixes from the
//front of a fictitious part number. Get leading spaces as well
//as prefixes beginning with A,B, or C

string strpartNumber = " b1030";

char [] aremoveTheseCharacters = {'A', 'B', 'C', ' '};

string strstrippedPartNumber =
strpartNumber.ToUpper().TrimStart(aremoveTheseCharacters);

MessageBox.Show (strstrippedPartNumber);
}

Results:
PartNumber " C1030" becomes strippedPartNumber = "1030".
Passing " b1030" trims to "1030".
And of interest, "abc1030", "ccc1030" and "a b c 1030" all return "1030".
"Z1030" would not be trimmed.

Array-Trims are still flawed:

This is well-and-good but not as useful as it would seem. Consider these variations of a
user-typed phone-number. Assume the program needs to remove all punctuation, both at
the margins and internally:

(208) 383-1234
208-383-1234
208.383-1234
(208383-1234
208/383-1234

with a desired result: "2083831234".

Chapter 4 - String Manipulation Page: 201


Trimming with an array of dashes, dots, spaces and parenthesis will fail because trim does
not process interior positions. For example, this array would fail. Note the double-
backslashes (which represents a single '\'):

//This array fails to trim phone numbers properly:

char [] aremoveTheseCharacters =
{' ', '(', ')', '-', '.", '/', '\\'};

To clean these numbers, more advanced tools are needed and they are discussed next.

To properly format a phone-number, you need to remove (trim) all user-punctuation, then
insert your standard punctuation back into the string. A fabulous method that does just
that can be found in Chapter 21, Formatting.

Chapter 4 - String Manipulation Page: 202


char.IsNumber ( )

Regardless of the function's name, char.IsNumber examines character or string data for
numeric values (zero-9) and returns a boolean "true" if it contains all numbers. The
command is almost always used with an if-statement:

if (char.IsNumber ('9')
{

if (char.IsNumber ("123A", 4))


{

There are two variations on the command:

char.IsNumber (<character data – examines a single char-byte>)


char.IsNumber (<string data>, <required index number>)

As you will see, this function is too limited for most real-world
problems. Read through this section for more information and
other possible solutions.

Simple char.IsNumber Test:

Consider this simplistic example, which examines a character 'A' to see if it is numeric.
This version only looks at a single character:

Example: char.IsNumber(char-data), completed but not particularly useful


private void button1_Click(object sender, EventArgs e)
{
char testChar = 'A'; //Notice the tic-marks for char data
//char data can only be one character

if (char.IsNumber(testChar))
{
MessageBox.Show
("This is numeric: " + testChar.Convert.ToString());
}
else
{
MessageBox.Show ("This is not numeric");
}
}

Chapter 4 - String Manipulation Page: 203


Testing String Variables:

char.IsNumber has an "overload" that can examine strings instead of a single-characters.


"Overloads" are explained in the next several chapters but they basically a variation of the
original command and the variation accepts different parameters.

As you are typing the "char.IsNumber(" command, the popup-help shows a list of
variations in a scrollable list.

'IsNumber' has 2 overloads, where the first has the least number or parameters and the
second is slightly more complicated. Click the arrows to view each variation. The second
overload shows it returns "boolean" and accepts a passed string, along with a required
index/numeric value.

When you are reading an overload, the parameter list always and
only shows "required" variables – none are optional – but there
are usually multiple overloads, each with their own list. This
way Visual Studio does not have to document all variations on
required and optional parameters. Each entry is a self-contained
list of all required values.

Consider this routine that tests the first three characters in a string for "numeric-ness":

Chapter 4 - String Manipulation Page: 204


Example: Examine only the first 3-characters for numeric-ness, complete
string strTest = "123 Elm";

//Check the first three digits for numbers.


//Use an integer-index value of 2 – a base-0 count

if (char.IsNumber(strTest, 2))
MessageBox.Show ("The first three digits are numeric");
else
MessageBox.Show ("The first three digits are not numeric");

//A better example would use an IndexOf command to find the


//first space and use that as the numeric counter. This idea
//is discussed below.

where:

• The char.IsNumeric's "string" overload requires a string and an "index" (a numeric


value) before it will operate. The index tells the command how many characters to
test against.

The numeric value is listed as an "index," which in Visual Studio means a numeric
count, starting at zero – base-0. For example: char.IsNumber ("123 Elm", 2) only
tests for numeric against the first three digits. The index starts counting at position
zero. 2 is the third digit.

• Test the entire string ("123 Elm") by using the string's total length. In Visual
Studio/C#, lengths are always calculated as base-1 (by design), which starts
counting at "1". Comparing a base-0 to a base-1 count means you have to off-shift the
counts by one:

"123 Elm" is 7-long when counting base-1


but is 6 when using an index base-0

char.IsNumber requires an index and to test the entire length, take the calculated
length minus one: char.IsNumber ("123 Elm", 7 - 1 = 6)

Thus: if (char.IsNumber(strTest, strTest.Length -1))

j In C#, all indexes are base-0; all lengths are base-1.

A semi-real-world example would find the first space-character's position in an address


and would use that count as the index. This example uses a new "find" method called
"IndexOf", which is described shortly.

Chapter 4 - String Manipulation Page: 205


private void button1_Click(object sender, EventArgs e)
{
string strstreetAddress = "123 N. Elm St.";
int ifoundSpacePosition = strstreetAddress.IndexOf(' ');

if (ifoundSpacePosition > 0)
{
if (char.IsNumber(strstreetAddress, ifoundSpacePostion -1))
{
MessageBox.Show("Address has numeric prefix");
}
else
{
MessageBox.Show("Address not numerically prefixed");
}
}
else
{
MessageBox.Show("No space detected");
}
}

Limitations with char.IsNumber:

Interestingly, this is the only numeric test available in C# and it is limited in what it
detects. For example, char.IsNumber claims these string values are not numeric:

"-54"
"54-"
"6.2431"
"$54.12"

And what about these user inputs: should they be considered numeric?
"1,800.23"
" $100"

Should there be an option to allow them to be considered as numeric? The util.Chapter


addresses these concerns with a more powerful tool that can work around these problems.
In that chapter, see the functions "IsNumeric" and "IsNumbers."

Chapter 4 - String Manipulation Page: 206


<string>.Replace( )

The ".Replace" method replaces any string with another string and the general syntax is:

<string-variable>.Replace
(<search-original-string>, <new replacement-string>)

Example: <string>.Replace, complete


private void button1_Click(object sender, EventArgs e)
{
//Replace "Robert" with "Bob"
string strtest = "Robert Smith";

MessageBox.Show(strtest.Replace("Robert", "Bob");
}

Results: Starting with "Robert Smith", replacing to "Bob Smith"

where:

• Search and Replace strings are case-sensitive and the search must match exactly.

• Wild-cards are not supported (*, ?).

• Each (multiple) occurrence of the same search-string is replaced.


For example, replacing the letter "A" with "C" in "AAA Autobody"
results in "CCC Cutobody".

• This method does not have a way to replace just the first or last occurrence; use the
"substring" commands or Chapter 6's util commands.

• Search and replace strings can be differing lengths.


For example, replace "Robert" with "R.".

• The example above does not change the original string; it only replaces for the
duration of the MessagBox statement. To replace the original, re-assign strtest to
itself, as in: strtest = strtest.Replace("Robert", "Bob");

Parts of a string can be removed by replacing the Search-string with an empty-string. This
is most commonly used when the results are written back over the top of the original
string:

Chapter 4 - String Manipulation Page: 207


Replacing a string with an empty-string, complete
private void button1_Click (object sender, EventArgs e)
{
//Replace the letters "Q." with nothing
//Note the extra trailing space in the test, which
//helps isolate middle-initials. A better test might have
//been " Q. ".

string strtest = "John Q. Public"


strtest = strtest.Replace ("Q. ", "");

//Display the results in a TextBox:


textBox1.Text = strtest;
}

Results: Replacing "Q. " in "John Q. Public", resulting in "John Public".

Chapter 4 - String Manipulation Page: 208


Character to Numeric Conversions

Programs often need to convert string (character) data, usually from external files or from
data-entry text-box fields, into a pure numeric value. Numeric conversions are required
before mathematical operations can be performed. These operations are the opposite of
the Convert.ToString seen earlier and work in a similar fashion.

Syntax and Variations:

Convert.ToInt32( ) Convert to Integer


Convert.ToSingle( ) Convert to Floating point – numbers with decimals
Convert.ToDouble( ) Very large or precise Floating Point numbers

Consider this code, which converts a string "123" to a numeric value, then adds 10, then
another 8:

Example: Convert.ToInt32( ), unsafe – Needs a try-catch


private void button1_Click (object sender, EventArgs e)
{
string strInput = "123"; //Note the quotes – string data
int iNumber;

iNumber = Convert.ToInt32(strInput);

iNumber = iNumber + 10; //Adds 10 to 123


iNumber += 8; //Adds an additional 8 to 133

//Ironically, convert the number back to string in order to


//to see it in a MessageBox:

MessageBox.Show (Convert.ToString(iNumber));
}

where:

• Convert.ToInt32(string-to-convert) explicitly converts the string to a number and in


this example, results are assigned to a new variable before other steps act upon the
conversion. The conversion could also be in the middle of a statement or part of an
intermediate calculation, as in:

MessageBox.Show
("Intermediate answer: " + (Convert.ToInt32(strInput)+10);

• iNumber += 8; is short-hand for "iNumber = iNumber + 8"


(e.g. take whatever is in iNumber and add 8, then re-assign the value back over the top
of itself - iNumber).

Chapter 4 - String Manipulation Page: 209


• Dozens of other conversions are available, but probably most commonly include:
Convert.ToSingle( ) (think Convert to Float – numbers with decimals)
Convert.ToDouble( ) (think Large or very precise Float)

Comments on Numeric Conversions:

If the input string contain nonsensical data (123 N. Elm), you will incur the wrath of a
runtime error "Input string was not in the correct format." Use a try-catch to intercept
these types of data-problems; see "try-catch Exception Handling" near program 4.4,
below. Later chapters show more sophisticated techniques.

Notice a string, such as "123.10" cannot directly convert to an Integer (it fails with an
"Input string was not in the correct format" because of the period). To convert, first
move the variable to a floating-point number then to an Integer (truncating the fraction);
see below.

Converting to Other Numeric Types:

Converting from an integer to a floating point number is acceptable because the jump
from "123" to "123.00" is a lossless-change. In instances like these, C# allows "implicit"
conversions without question and the conversion can be made with a simple assignment
(equal):

Example: Implicit, lossless Conversion from Int to Float, complete


//Implicitly convert from Float to Integer, no problem:

int iNumber = 123;


float floatNumber;

floatNumber = iNumber; //Convert to float is implicit at assign

But converting from a floating (123.10) to integer (123) risks truncation and the compiler
will not allow it unless explicitly converted. Notice how floating-point literals are
suffixed with an "F":

Example: Convert Floating Point to Int, Explicitly Truncating


//Explicitly convert when data-integrity is at risk:

float floatNumber = 123.10F;


int iNumber;

iNumber = Convert.ToInt32(floatNumber); //Results: "123"

Chapter 4 - String Manipulation Page: 210


Conversions with Casting:

Numbers can also be converted to a new type using a technique called "casting". For
example, an integer can "cast" to a floating-point number and visa-versa. This is best
described with an example:

Example: Casting to an Integer, complete


float floatNumber = 123.1F;
int iNumber;

// Casting: These next two statements are equivalent.


// Both cast a floatingpoint number as an integer before assigning
// it to the variable:

iNumber = (int) floatNumber;


iNumber = Convert.ToInt32(floatNumber);

where

• (int) <variableName> is the casting statement – the word "int" in parenthesis. When
casting, prefix the variable with parenthesis and the type of variable you want it cast
to. The syntax is odd because the parenthesis surround the first part of the statement,
not the variable name.

• Cast as (int), (double), (float). Other types of objects beyond the scope of this chapter
can also be cast.

• Note the "F" in the "floatNumber = 123.1F" declaration. When initializing a floating-
point number as a literal, suffix the declaration with a capital F, letting the compiler
know this is a floating-point number. ('float' and 'single' are the same. More details in
the next chapter.)

• You can cast one type of number to another. But you cannot cast a string to a
numeric data type. Instead, use "Convert.To" – assuming the string is a valid
number.

Casting is Confusing:

When casting is and is not allowed can be confusing. As you research other people's code
on the web, you'll find that some will cast variables from one data-type to another – but
the idea for casting was really designed for changing classes and other object-oriented
features, none of which have been discussed yet. At the risk of over-simplification, I'll
generalize: For commands discussed in this chapter, cast as part of an intermediate
calculation or when moving from similar data-types. Use Convert.To if in doubt or are
having problems with casting. Convert.To is always a safe command to use. In any case,
the compiler will tell you if a cast is not allowed.

Chapter 4 - String Manipulation Page: 211


null and Empty Strings

Strings hold character data but they can also hold "nothing" – that is, they may be empty,
undefined, null, or they may have spaces. Unfortunately, with several types of
nothingness; testing for them is problematic and most programmers struggle with this.
This section shows how to best detect these states and it demonstrates a powerful tool that
Microsoft does not supply.

This section, along with Chapter 6, demonstrates the tools needed to tackle this subject
with skill, but in order to learn what is needed, I will be taking you on a scenic tour of the
available commands. There are a litany of suggested tests on the web and most have
subtle flaws.

Flawed ways to detect null and empty strings:

if (strtest == null)
if (strtest == "")
if (strtest.Trim() == "")
if (strtest.Trim().Length == 0)
if (strtest != null && strtest.Length > 0)

Microsoft's recommendation:
if (string.IsNullOrEmpty(<strstring>))

This book's recommendation:


if ((strtest + "").TrimStart().Length > 0) **Recommended

Even better, write two new functions (methods) to make this


test even easier. See Chapter 6:
IsBlank(strtest)
IsFilled(strtest)

Detailed Types of "blank" fields:

There are several states of "emptiness," each of which is different. Here is a string
variable in various states of declaration:

string strTextA; declared but not initialized; value null


string strTextA = ""; initialized with an empty-string (quote-quote)
string strTextA = string.Empty; same as an empty-string
string strTextA = " "; initialized with a space (unprofessional)
string strTextA = null; initialized with null – not an empty string

Declared but not Initialized:

When a variable is declared but not assigned a value, it contains a null. A null is not an
empty string nor is it a zero. It means the value is undefined or unknown.

Chapter 4 - String Manipulation Page: 212


Un-initialized variables cannot be used to populate any other variable or function; if you
try you'll get a compiler error: "Use of unassigned local variable <variable name>". This
is easy to detect because the compiler sees them while editing and these problems are seen
long before the end-user arrives at the finished program. To resolve, assign a value when
the variable is first declared – typically a quote-quote "" (or if a numeric variable, the
number zero is assigned).

string strtest = ""; //Assign an empty-string to initialize the


variable
int imyVariable = 0; //Assign a zero or other value to numeric
variables

Empty Strings:

Strings that are initialized or assigned "empty-strings" are also different than nulls or
undefined. An empty string is where the variable is assigned the value = "" (two double-
quotes – "quote-quote" or alternately with string.Empty(stringname)). An empty-
string is just what the name implies: it is a string with nothing in it. From the compiler's
point of view, this is a perfectly good string. textBoxes default to this value when first
created. The compiler considers this populated with data even though there is no "real"
data.

null:

"nulls" are strange from a data-processing view. They are not empty strings, nor are they
a numeric zero; technically, a null indicates no value was specified. Un-initialized
variables are null and often databases populate unknown values with nulls and certain
array operations, when they have nothing to work with, generate nulls. When most C#
commands encounter one, they implode. Because they are not a string or a number, they
have to be tested carefully and separately.

Spaces:

Setting a field's value to one or more spaces is mentioned because end users sometimes do
this in data-entry fields – that is, they type a space or a line of spaces in order to "blank
out a field." Because of this, you often receive an exported spreadsheet or file with field-
spaces. This type of data is a nuisance to program around. Because a space is an actual
value, it eludes all empty-field detection and yet for all practical purposes, the field is
empty. The IsBlank and IsFilled functions, described later, navigate this problem with
grace. In your code, do not initialize or assign variables with a space-character (quote-
space-quote) just to "clear them out." Use "" or string.Empty().

nulls are Problematic:

Nulls are a nuisance. If you suspect either a null or an empty string in the data, especially
if it comes from an outside source, you will have to test separately for each possibility.

Chapter 4 - String Manipulation Page: 213


Consider this code, which takes an integer, which is being simulated with a null, and tries
to add +1:

int numberA = null;


MessageBox.Show(Convert.ToString (numberA + 1));

Result: Compiler error: "Cannot convert null to 'int' because it is a value type." [this is
an implicit conversion]. The trouble is, this error can also happen at run-time and it will
cause the program to crash, long after it was compiled.

Many string functions also panic with nulls. For example, a string.Length against a null
value gives an error, "Cannot convert null to 'int'". Although empty strings have a
(numeric) length of zero, null strings do not have any length – not even zero. In general,
nulls cause all kinds of problems and they must be treated with care.

First-blush: Testing for nulls:

One way to resolve these problems are to test for null prior to any calculations.

if (strText == null) or
if (String.Compare (strText, null) == 0) //(Compare returns -1,0,1)

This adequately tests for null, but it does not solve the most likely problems encountered
in a program. Typically, the code needs to concern itself with more than a null value.
Empty-strings, null-values and "blank" fields all cause problems.

First-blush: Testing for empty and space-strings:

Testing for empty strings is relatively easy (remember, these are not "null" strings). Test
explicitly with an empty-string (quote-quote) or by checking for a zero-length. However,
these tests explode3 if they should happen to encounter a null value.

if (strTest == "")
if (strTest < " ") //Illegal: (Use String.Compare)
if (strTest.Length == 0)4

Testing for empty strings using .Length is favored by many programmers and you will see
numerous examples on the web. But because the test explodes when it encounters a null,
it is too dangerous to use in the real world.

Testing "space" strings (quote-space-quote – usually typed by end-users in data-entry


fields) is a more problematic. A .Length==0 test fails because the space counts as a
character and it fails because the number of spaces are not known – e.g. " " (x1 space) and
" " (x3 spaces) look the same on the screen.

3
"Explode" means a nasty run-time error - one that happens after you release your final program to
production – users will call.
4
If testing a Form field, a similar test would be if(textBox1.Text.Length == 0)

Chapter 4 - String Manipulation Page: 214


These problems can be worked-around with these next two tests but keep in mind neither
detects nulls or empty-strings, which are equally likely:

if (strTest == " ") //Ineffective and pointless

if (strTest.Trim() == "")
if (strTest.TrimStart() == "")
//more reliable test for multiple spaces but
does not test for nulls or empty-strings

The Trim tests are particularly interesting because it removes any number of leading
spaces that might have been typed by end-users before comparing with an empty-string,
however, it still dies if it finds a null.

Painful Ways to Test for Empty and Nulls:

To effectively test for an "empty" field – that is a field that contains nulls, empty-strings,
or blanks – you must combine several tests. The key is to test nulls separately, keeping
the rest of the routine from abending5.

if (strTest == null)
{
//The field is "null" and must test separately before a trim
//is attempted
}
else
if (strTest.Trim() == "")
{
//The field is "empty"
}
}

This test correctly checks for nulls and spaces but involves a half-dozen lines of code.
Since most programs frequently need to test for "blank" fields (data-entry fields, database-
fields, etc.), the test is too tedious.

String.IsNullOrEmpty( ):

If this is starting to look complicated, you are right. Using the techniques above, testing
for spaced, blank, empty and null strings each requires a different test. Starting with C#
version 2005, Microsoft agreed and developed a new boolean string method for these
scenarios.

Consider this code, which uses the new "String.IsNullOrEmpty( )" function:

5
Abend = Abnormal End; also known as a run-time error, an "Exception," or more commonly as a
program crash. These types of errors cause your program to die while end-users are using the program
and these are often hard to debug.

Chapter 4 - String Manipulation Page: 215


Example: String.IsNullOrEmpty, Microsoft's recommendation
private void button1_Click (object sender, EventArgs e)

//This is Microsoft's recommendation for testing null and


//empty strings, but it does not go far enough

string strTest = null; // The sample value being tested

if (String.IsNullOrEmpty(strTest))
MessageBox.Show("No data");
else
MessageBox.Show ("String has data");
}

where:

• Use the key phrase "String.IsNullOrEmpty( )" with a variable as a parameter within
the parenthesis.

• Much simpler than a nested-if.

The only issue I have with this statement is a space (" ") is considered an occupied data
field. You always have to remember those pesky users who pad fields with spaces in
order to clear them. For most programs, this should still act like it was an empty field,
even though it has 'valid' data within. Although this is a good first-step, the "IsBlank" and
"IsFilled", introduced below, solve this problem.

Nullable Tests:

As an aside, C# also supports a "nullable data-type," which is a value that can contain
either legitimate data or a null, and this can only be applied against numeric data types
and certain constructs, such as an array. The reason for using a nullable data-type is
because sometimes a (numeric) value is not known.

Consider what should happen when importing integer temperature values from a database.
What if the value was never set (e.g. a null). Should your program store a zero (0) – that
would not work because zero degrees is a valid temperature. If you attempted to store a
"null" to a standard integer, the program would either not compile or would crash at run-
time. Similarly, how should unknown dates, such as a birth-date, be represented when the
date-field absolutely requires a date? Storing a null would normally cause the program to
fail. This is where a "nullable data-type" is useful; it can store legitimate numbers *or* a
null.

A nullable data type is defined with a declaration: Nullable<variable>, but nobody


does this; instead, they use a question-mark, as in:

Chapter 4 - String Manipulation Page: 216


int? imyNumberVariable = 10;
double? d1 = 3.14;
char? myCharacter = 'z';
DateTime? myDate = null;
int?[] aimyNumberArray = new int?[10]; //An array of integers
string? myString; //Not allowed – not a nullable type

where the question mark indicates this data-type is a "nullable" type and the variable can
hold a null value. Note: "Strings" are not nullable (with a question-mark definition), but
they can be null – but you cannot use the ?-technique.

Once defined as "nullable", null values can be tested with a property called ".HasValue".
This property will not work on a standard integer, date or string – it must be defined as
"nullable" before this property can be used.

In a contrived and simple example, testing with the .HasValue property could look like
the following code and the if-statement prevents to program from having an "Exception"
(crash). You could also test for a not-equals-null (!=null), with similar results:

int? imyNumberVariable = null; //simulated; Note the question-mark

if (imyNumberVariable.HasValue)
imyNumberVariable = imyNumberVariable + 100;
else
{
//You cannot use this variable
}

Null-Conditional Operator:

Starting in Visual Studio 2015, a new "null-Conditional operator," which is represented as


a "?." (question-mark+period), became available6. With this new operator, the null-test
can be condensed, discarding a =null or .HasValue if-statement.

Consider this example, where a null-string is accidentally being passed into a substring
routine. If a null is encountered, a null is immediately returned and the remainder of the
command is not interpreted. Without a test (and without the ?.), this would normally
cause the program to abend:

private void button1_Click (object sender, EventArgs e)


{
//Accidentally set a string to null, say from some data-source...
string strSomeString = null;

//Attempt to substring the first four characters...


//Without the "?.", the substring would abend

MessageBox.Show
("The value is '" + strSomeString?.Substring(0,4) + "'");
}

Results: The value is ''. The Substring command is abandoned. The closing apostrophe is
appended to the MessageBox.

6
See also the Terniary ? command in Chapter 3.

Chapter 4 - String Manipulation Page: 217


More commonly, the null-test happens in a downstream function. Consider this example,
where a 'function' called "A100" needs to substring the first 4 characters from a string
(functions are described in Chapter 6):

private void button1_Click (object sender, EventArgs e)


{
//Accidentally set a string to null, say from some data-source...
string strSomeString = null;

//Call a routine (below) to do the work


string stranswer = A100_Routine (strSomeString);
MessageBox.Show ("The value is '" + stranswer + "'");
}

private string A100_Routine (string strpassedValue)


{
return strpassedValue?.Substring(0,4);

//This would abend if a null were accidentally passed, but the


//Null-Conditional Operator (?.) prevents the failure. Notice
//an if-statement is not required
}

This test is succinct and works well for a null-value, but the routine has other failures
which are not accounted for. If an empty-string is passed, or if the string is shorter than 4
characters, the program would still abend, but the intent of this section is to quickly
demonstrate the new 2015 operator.

Often, what is really needed, is a way to ask a question of the data, asking if the string is
null, empty or spaces - e.g. what I call "blank". This is discussed next.

Chapter 4 - String Manipulation Page: 218


Testing for "blank"

My belief: For most purposes, a null, empty-string, or spaces, all


mean the same thing – there is no data in the field. This is what I
call a "blank" field. This is non-standard nomenclature however,
'blank' or 'filled" are the terms I will use throughout the
remainder of this book. Technically, a field with spaces is not an
empty field – it has data – but more often-than-not your program
needs to behave as-if it were an empty field.

From the previous section, deciding when a field is "blank" requires three different tests:
• Is it null?
• Is it an empty-string?
• Is it only "spaces"?

In order to keep the program from crashing, all three conditions must be tested, in a
specific order, and it requires a tedious nested-if or a mixture of IsNullOrEmpty and other
checks, but there are coding tricks that can simplify this task. In this section you will
build a "function" to automate this idea. The next chapter takes these new functions and
puts them into a library, where they are always available. The final code will be easy-to-
write and more importantly, easy-to-read.

Below are several methods that test for "blank" strings; some
designs are better than others. You are encouraged to try them
in order to learn more about if-statements and logic. Try the
examples in your test program using button1_Click's event.

Checking for blank strings using a compound if-statement:

Using the techniques described in the previous sections, the null and .Length test can be
combined into one semi-complicated statement:

if(strTest != null && strTest.Length > 0)

This method is flawed because it doesn't test for a space-character and it relies on a oddly-
worded phrase "not-equal null (!=)" It also uses a sneaky double-ampersand, which first
tests for nulls before attempting the other tests – saving a possible abend. As a reminder,
Chapter 3 discusses the usefulness of the double-ampersand in this types of test. Both of
these "tricks" require mental gyrations to understand – but this is not a bad first step.

Adding another layer to test for "space-characters" would involve adding an "and" or "or"
clause to the command above and it would make the statement un-interpretable to most
humans. However, this idea is still worth exploring. Try this code by doing the
following:

Chapter 4 - String Manipulation Page: 219


Note:
In order to compile a test program, you must declare and initialize the string variable,
setting it to null. (Although un-initialized string variables are set to null, the compiler will
not let you run the program in that state. To work around this, the code forces a test
variable to null in order to simulate an uninitialized variable.)

1. In the button1_Click event, begin with this code:

private void button1_Click (object sender, EventArgs e)


{
string strTest = null; //You must explicitly set to null for test
//This is a simulation of a real-world event

// <more code below>


}

2. Create an if-statement that checks for both null and for string.Length.
Using a trick from previous chapters, use an AND statement within the if-statement
clause.

j Recall the double-ampersand first checks for null and if that fails, it doesn't resolve
the second-half of the "if", saving you from a run-time /abend.

Flawed test for blanks; but still interesting


private void button1_Click (object sender, EventArgs e)
//Test using a double-ampersand; this is actually a neat trick
//but still flawed because it does not test for spaces.

string strTest= null; // the sample value being tested

if (strTest != null && strTest.Length > 0)


MessageBox.Show("String has data");
else
MessageBox.Show ("No data");
}

Run the program with strTest as a null: Results "No data" -good
Change strTest from a null to an empty-string (""): Results "No data" -good

Change strTest to a (space): Results: "String has data". This is not the result wanted.

Chapter 4 - String Manipulation Page: 220


Best Practice: Test for blank strings using Trim-Append-Length:

The tests above have flaws in either their logic or complexity.


Here is the best way to test.

Best Practices Summary:


if ((strtest + "").TrimStart().Length == 0)

• Trim leading spaces first


• Append an empty string before testing; killing nulls
• Finally, check <string>.Length = 0 to detect empty-strings

Checking only a string's length is a common but flawed way to tell if a field is empty. It
works well on filled and empty strings, which have zero lengths, but crashes when the
string has a null value or is filled with spaces.

The null-value problem is solved using a trick: Take the string being tested and append
an empty-string; this immediately (and temporarily) converts the null to an empty string.
Once this is done, a Trim (to take care of leading spaces) and a simple string.Length,
makes a reliable "blank" test.

Best Practice: Testing for 'blank' strings


private void button1_Click (object sender, EventArgs e)
//This method is a best-practice method for testing blank data.
//The test properly detects nulls, empty and space-strings, but
//should be placed into a separate method, such as "IsBlank".

string strtest = null; // The sample value being tested

if ((strtest + "").TrimStart().Length > 0)


MessageBox.Show("String has data");
else
MessageBox.Show ("No data");
}

Conversely, the test could also be written this way:

Best Practice: Testing for 'blank' strings, alternate


if ((strtest + "").TrimStart().Length == 0)
{
MessageBox.Show("The string is blank");
}

Appending an empty-string is the key to this test and this protects the statement from
nulls. When an empty-string ( "" ) is appended to most other strings, the original string's
value doesn't change.

In other words, "Dog" + "" still equals "Dog".


If the original string were empty ( "" ), it would remain "".

Chapter 4 - String Manipulation Page: 221


nulls are the only fields changed by this operation and they get promoted to an empty-
string – simplifying the test.

j The key point is this: The null-step must be done before either the Trim or the .Length
calculation because both panic when they see a null.

In the middle of the test, "TrimStart" resolves the problem with "space" fields by
removing the "redundant spaces." Use a "TrimStart" instead of a more normal "Trim" for
a slight efficiency because there is no need to trim the back-side when you are only
looking to see if the string has a space. Words such as " " (space) or " Dog" (space-dog)
are correctly interpreted.

With the code above, change strtest and try these possibilities. All are detected as
"blank":

• "" (quote-quote)
• "bob" (Normal string)
• " bob" (leading space or spaces)
• "" (a single-space, quote-space-quote)
• " " (several spaces)
• null

The Best Solution for Checking Blank Fields:

All but the simplest programs have to check for "blank" data, usually in multiple
locations. But adding this best-practice-logic in so many places is cumbersome and would
soon become a chore. A better solution is to call a function that returns a true/false when
ever the function were asked, "is it blank?" Chapter 6 describes how to make this a
generic function but here is a hint on what it would look like in your program:

//This test is not yet developed; see Chapter 6


if (IsBlank(strTest))
{
<do stuff if blank>
}

The converse could also be asked:

//This test is not yet developed; see Chapter 6


if (IsFilled(strTest))
{
<do this stuff if it has data>
}

Starting with Chapter 6, you will be able to coin your own function names, essentially
creating your own keywords in C#. "IsBlank" and "IsFilled" are functions that C# should
have, but doesn't. These will become two verbs that you can't live without.

Chapter 4 - String Manipulation Page: 222


Finding Strings - IndexOf

Often, programs need to parse through strings, looking for information. For example,
assume your program reads an input file (perhaps an INI file), where a person's name is
listed with a keyword "Name" followed by an equal-sign-delimiter:

When parsing the line, you need either a delimiter or a known horizontal position in order
to snarf the information. The delimiter (in this case, the equ