Language Primer
You have a few language choices available to you as a .NET programmer: Visual Basic, C#, C++, or F# are at the core. There are also user interface (UI)-specific languages and markup syntax, such as JavaScript, HTML, and XAML. Which core language you choose is typically a result of your history, style, and intent. Developers who have worked with past incarnations of Visual Basic or another basic language will find they are at home inside Visual Basic. The language (including templates, tools, wizards, and so on) is all about developer productivity. Developers whose roots are in a C-based language (C++, Java, and so on) and who want similar productivity in a straightforward way gravitate toward C#. Of course, some developers will just want to stay in C++ even for their .NET applications.
Visual Studio 2010 saw the introduction of the F# language. Now part of the full Visual Studio product line, F# 4.0 targets enterprise developers. Similar to other .NET languages, F# supports object-oriented programming. What makes it different is that it is also a functional programming language. Functional programming elevates functions to first-class values. (The F in F# is for functional.) For example, a functional language allows you to easily pass functions as parameters, return functions as the result of a function, chain functions together to create new functions, create recursive functions, and more. These powerful features in F# allow you to more easily tackle complex algorithms with less code (and often less pain) than it would take with the standard object-oriented (OO)-only languages of Visual Basic and C#. Having F# inside of Visual Studio also means that you can leverage the .NET Framework, get the benefits of the Common Language Runtime (CLR) (including calling to and from other .NET code), and have debugging and other related tools support.
Programming Objects
Programming in .NET is an object-oriented experience. You write your own classes and leverage those created by Microsoft (forms, controls, and libraries). In fact, every .NET application has at least one class, and more often it has hundreds. You can extend classes with new functionality (inheritance), define classes based on a contract (interface), and override the behavior of existing classes (polymorphism). This section looks at defining objects with .NET code.
Classes
Think of classes as the container for your code. Classes define how you hold data (properties) and perform actions (methods); they communicate how your class works after it’s created (instantiated). When you create an instance of the class, it is an object and can actually maintain its own state and execute code to update and give access to it.
You define a class using the Class keyword. The following shows an example.
C#
public class Employee { }
VB
Public Class Employee End Class
Fields and Properties
You add code to a class to define its data and behavior. Data for your class can be stored in fields or properties. Fields and properties are similar; both define data that is contained in the class. The difference is that properties can provide a means to protect the access (setting and getting) to field data. Fields are typically private variables defined by the class, accessible only from the class code, and defined as follows.
C#
public class Employee { private string _name; }
VB
Public Class Employee Private _name As String End Class
You can define public fields on your class to make them accessible from any other class code. However, it is a best practice to encapsulate public data inside a property. This way you can control whether to expose the ability to read or write the value of a property. Properties are typically backed by an internal, private field. This is called data hiding and is implemented with the Private keyword. For example, the previously defined field can be encapsulated into a property as follows.
C#
public class Employee { private string _name; public string Name { get { return _name; } set { _name = value; } } }
VB
Public Class Employee Private _name As String Public Property Name() As String Get Return _name End Get Set(ByVal value As String) _name = value End Set End Property End Class
You can also create read-only properties. This is useful when you want to reserve the writing of the property’s value to code running inside the class. You create a read-only property by not implementing the set statement in the property definition. In Visual Basic, you also have to add the ReadOnly keyword. For example, suppose you want to add an Id property to the Employee class defined previously. This Id can be read by anyone, but its value (backed by _Id) is only set by internal class code. You could implement a read-only property as follows.
C#
private int _id; public int Id { get { return _id; } }
VB
Private _id As Integer Public ReadOnly Property Id() As Integer Get Return _id End Get End Property
Methods
Methods represent the blocks of code in your class that, when called, perform some specific action. This action could be reading or writing from a database, calling other methods, calculating a value, processing some business rules and returning a result, or whatever you need your code to do.
Methods are defined by their names and access levels; see the next section for more details on access levels. In Visual Basic, you also need to add the Sub keyword to define a method that does not return a value. In C#, this is done by indicating the return type of void before the method name. For example, if you were to add a Save method to the Employee class previously defined, the code would look like this.
C#
public void Save() { //implementation code goes here }
VB
Public Sub Save() 'implementation code goes here End Sub
Methods often return values to the code that called the method. To define a method that returns a value, you must indicate the method’s return type (the class type of the returned data). In Visual Basic, you also use the keyword Function (instead of Sub). You use the Return keyword to indicate the value to return from your code. For example, if you were to add a method to calculate an employee’s remaining sick day, you would do so as follows.
C#
public int GetRemainingSickDays() { int _sickDays = 0; //calculate remaining sick days return _sickDays; }
VB
Function GetRemainingSickDays() As Integer Dim _sickDays As Integer = 0 'code to calculate remaining sick days Return _sickDays End Function
In this example, note the return type defined in the method signature (first line of the method). Also note the use of the keyword Return to return a value from the method. In this case, that value is stored inside a variable defined as internal to the method.
Member Accessibility
The properties, fields, and methods in your application are referred to as class members. Each member in your class is defined to have a specific access level. As you’ve seen, if you want other classes to be able to access a member, you must declare that member as public. If you want to reserve the member for accessibility only within the class, you declare it as private. These are two of the member accessibility levels available to you. The full complement of accessibility levels is described in Table 3.1.
TABLE 3.1 Member Accessibility Level in .NET
Level |
Description |
Public |
Indicates that a member is publicly available to any code outside of the class. |
Private |
Indicates that the member is hidden and private to the class that contains the member. No code outside the class can directly access members defined as private. |
Protected |
Protected is similar to private. It indicates that the member is not exposed publicly. Rather, it is private to the class. However, protected members are also made available to any class that derives from the class that contains the protected method. (See the “Inheritance” section for more details.) |
Internal |
Indicates that a member is available to all code within the assembly that contains it. This means other classes within a compiled .dll or .exe can access the member. However, other assemblies that reference a given .dll cannot access its internal members. |
Protected Internal |
Indicates that the member is accessible by all code within an assembly and any code that derives from a class that contains the given member. |
In addition to class-member accessibility, classes themselves use the same accessibility levels. You can declare a class as public, private, protected, and so on to define your intended usage. You want to make many classes private or protected to the class and deriving types. The classes you make public define the functionality you want to expose to other code.
Constructors
A constructor is code that is called when a new instance of your class is created. This code is used to define how you want your class instance initialized, typically by setting default values or some related setup code. You create a constructor as you would a method. The difference is that you give the constructor the same name as the class; you also do not need to prefix the method with void in C#. In Visual Basic, you can use the Sub New statement to create a default constructor for the class. This can be useful if you are initializing read-only properties. The following code shows an example for the Employee class.
C#
public Employee() { //init default values of an empty employee object }
VB
Public Sub New() 'init default values of an empty employee object End Sub
A class can have multiple constructors to change the way in which the object is initialized. In these cases, each constructor is defined with a different set of parameters. In C#, the version of the constructor that does not take parameters is referred to as the default constructor. In Visual Basic, the Sub New construct is used to define the default constructor. The following shows a couple additional constructors added to the Employee class. One initializes an Employee object based on the calling code passing in an id parameter; the other uses the employee’s email address to initialize the object.
C#
public Employee(int id) { //init default values for the employee defined by the given ID } public Employee(string emailAddress) { //init default values for the employee defined by the given email }
VB
Public Sub Employee(ByVal id As Integer) 'init default values for the employee defined by the given ID End Sub Public Sub Employee(ByVal emailAddress As String) 'init default values for the employee defined by the given email End Sub
Static (Shared in VB) Members and Objects
Sometimes you do not want the full behavior of a class for all your methods. Instead, you might want to define certain methods that are not part of an instance of the class. These methods often retrieve information or calculate values but are not part of a specific object. In these cases, you can create entire classes or just specific methods of a class as static (or shared in Visual Basic).
The Shared and static keywords, when applied to a method, indicate that the method can be called without creating an instance of the class that contains it. Shared and static can also be defined at the class level. In this case, you are indicating that the class only contains shared and static methods, and you cannot create an instance of it. For example, you might add a static helper method to the Employee class to check to see whether an employee is active in the system before you create an instance. This declaration would look like this.
C#
public static bool IsActive(string emailAddress) { //check to see if an employee has been added to the system }
VB
Public Shared Function IsActive(ByVal emailAddress As String) As Boolean 'check to see if an employee has been added to the system End Function
Enumerations
Enumerations enable you to create a group of named values that help improve your code readability. Each item in an enumeration is a unique numerical value (byte, sbyte, short, ushort, int, uint, long, or ulong). Note that the default is int. You can pass around the enumeration value as a name rather than an actual value. In this way, your code doesn’t rely on arbitrary, “magic” numbers. Instead, the code is sensible and readable.
You create an enumeration using the enum keyword. For example, you might add an enumeration to the Employee class to store the employment status of an employee. This would enable you to make decisions in your code based on the specific status of an employee. To define this enumeration, you add code as follows to the Employee class.
C#
enum EmploymentStatus { Salaried, Hourly, Contract, Other }
VB
Enum EmploymentStatus Salaried Hourly Contract Other End Enum
Inheritance
You can define a new class based on an existing class, which is called inheritance. You use inheritance to extend (or add to) the functionality of a base class. Classes that extend a base class are said to derive their functionality from another class. That is, they contain all the functionality of the base class plus any additional functionality added to the new class.
You indicate inheritance in Visual Basic by using the Inherits keyword; in C# you add a colon and the base class name following the name of the new class. For example, suppose you implement a Manager class that derives from Employee. The Manager class contains all the members of an Employee but might add special properties and methods specific to a Manager. You define this new class as follows.
C#
class Manager: Employee { }
VB
Public Class Manager Inherits Employee End Class
Note that you can actually define a base class that cannot be created. Instead, it only exists to form the basis for a new class. Other classes can derive from it, but you cannot create a direct instance of just the base class. This is done by adding the MustInherit (VB) or abstract (C#) keyword in front of the class definition. The keyword NotInheritable (VB) or sealed (C#) indicates that the class cannot be used as the basis for a new class.
Overriding Behavior
When you design your classes, consider how other developers might extend them. Your classes might serve as the base class for future derived classes. If this is the case, you might also consider which (if any) features of your base class you want to allow a derived class to override. The derived class may then implement a new version of one of your base methods, for example. This process is often referred to as polymorphism in OO programming.
To change the data or behavior of a base class, you can either add to the base class or override an existing member of the base class. Doing the latter gives you alternate behavior for the same function in your new class. You decide which members of your base class are available for override. You do so by marking them as virtual (Overridable in VB) members; this indicates that a derived class may override your base class functionality.
For example, suppose that you want to enable the CalculateYearlyCost method of the Employee class to be overridden when the Employee is used as the base for the Manager class. In this case, the calculation for a Manager is different for that of an Employee. You therefore mark the method inside the Employee class as virtual (C#) or Overridable (VB), as follows.
C#
public class Employee { public virtual float CalculateYearlyCost() { } }
VB
Public Class Employee Public Overridable Function CalculateYearlyCost() As Single End Function End Class
You can then override this method in the derived class. You do so using the override (C#) or Overrides (VB) keyword. You can still call the method on the base class if you need to by using the base (C#) or MyBase (VB) keyword. The following shows an example.
C#
class Manager : Employee { public override float CalculateYearlyCost() { //add new functionality, access underlying method using base keyword } }
VB
Public Class Manager Inherits Employee Public Overrides Function CalculateYearlyCost() As Single 'add new functionality, access underlying method using MyBase End Function End Class
Hiding Members
There is a second way you can override the functionality of a base class. It involves using the keyword new (C#) or Shadows (VB) to redefine the base method. Overriding in this manner hides the base class members. However, the base class member is still called if an instance of the derived class gets downcast to an instance of the base class. This type of overriding is referred to as hiding by name. For example, you could replace the C# keyword override with new or the VB Overrides with Shadows to implement this type of behavior.
You need to be careful about hiding members versus overriding because downcasting can occur often. For example, you might be working with a collection of Employee objects (some of type Manager and some of type Employee). If you iterate over the list using the base class (for each employee), you get a different method called on the Manager class depending on whether you hid the member (in which case, the base class method is called) or overrode the member (in which case, the derived class method is called).
Overloading Members
You can also create multiple versions of the same procedure. All versions of a procedure can be defined inside the same class, or you can have a few versions in a base class and yet other versions in a derived class. This is useful when you need to preserve the name of the procedure but need to create different versions that each take different parameters. Creating multiple versions of a procedure is called overloading or hiding by signature (as in the method’s calling signature).
Overloading a method must follow rules designed to make each overload somehow different from all the others. Of course, each overload has the same name. However, you must change either the number of parameters the method accepts, the data type of one or more of those parameters, or the order of the parameters. You create a valid overload by changing one or more of these items to make the overload signature unique. Note that changing the return type, if the method returns a value, is not sufficient to create an overload; nor is changing just a parameter modifier.
For example, suppose you were creating a method to return the number of vacation days left for an employee. You might allow the users of this method to get the vacation days left for the current year, a supplied month, or a supplied month and year. In this case, the users of your method see a single method with multiple overloads. You implement this overloading similar to the following code.
C#
public short GetVacationUsed() { //returns all vacation used in the current year } public short GetVacationUsed(short monthNumber) { //returns all vacation used in the given month of the current year } public short GetVacationUsed(short monthNumber, short year) { //returns all vacation used in the given month and year }
VB
Public Function GetVacationUsed() As Short 'returns all vacation used in the current year End Function Public Function GetVacationUsed(ByVal monthNumber As Short) As Short 'returns all vacation used in the given month of the current year End Function Public Function GetVacationUsed(ByVal monthNumber As Short, ByVal year As Short) _ As Short 'returns all vacation used in the given month and year End Function
Defining Interface Contracts
An interface is used to define a class contract. An interface does not contain any actual functioning code. Rather, it indicates a common structure for code that must be implemented by another class. This enables you to create common contracts and use those contracts across multiple objects. You can then trust that each class that implements the interface does so completely, following the outline of the interface.
An interface can define different types of class members, including properties, methods, events, and the like, but not fields or constructors. To create an interface, you use the Interface keyword. For example, suppose you want to define a basic interface for a person. The Employee class might then be required to implement this interface. Other classes (such as User and Customer) might also implement the same interface. The following shows an example of how you might define this interface.
C#
interface IPerson { string Name { get; set; } DateTime DateOfBirth { get; set; } string EyeColor { get; set; } short HeightInInches { get; set; } }
VB
Public Interface IPerson Property Name As String Property DateOfBirth As DateTime Property EyeColor As String Property HeightInInches As Short End Interface
You implement the interface by adding the interface to the class definition on the class where you intend to implement the interface. In Visual Basic, this is done by adding the Implements keyword under the class definition (similar to inheritance). In C#, you add the interface to the class definition the same way you would indicate a base class (using a colon). You can separate multiple implemented interfaces by a comma.
Creating Structures
So far we have talked about programming classes. There is another kind of type available to .NET programmers called a structure. Structures are similar to classes; they can contain properties, fields, enumerations, and methods. They can implement interfaces and can have one or more constructors. The main differences lie in how structures are managed by .NET.
Structures are considered value types. This means that when structures are used, the entire class instance is passed around as a value and not a reference. A class is a reference type. When you use a class instance and pass it around your application, you are actually passing a reference to a class instance. Not so with a structure. This also changes how .NET manages the memory used for structures and classes. Structures use stack allocation, and classes are managed on the heap by the Garbage Collector. To put this in perspective for .NET developers, imagine you have an instance of an Employee class. This instance might be created inside one object and passed to another object’s method. If the second object makes a change to the Employee instance, this change is reflected inside all objects that maintain a reference to the instance. If this were a structure, however, there would be copies of that object passed around, and changes would be isolated to each copy.
There are other differences between classes and structures. For one, structures are sealed; that is, they cannot be inherited from. They also have an implicit public constructor that cannot be redefined. For these reasons, structures are best used when you need a lightweight container for data values and do not need the features of a reference type. Structures are often used to define small custom data types, reducing the odds to trigger a garbage collection.
You define a structure much like you define a class. In place of the class keyword, however, you use struct (C#) or Structure (VB). For example, imagine you want to define a data type that represents a paycheck. You could create a structure to hold this information. The following shows an example.
C#
public struct PayCheck { private double _amount; public double Amount { get { return _amount; } } //add additional structure elements ... }
VB
Public Structure Paycheck Private _amount As Double Public ReadOnly Property Amount() As Double Get Return _amount End Get End Property 'additional structure elements ... End Structure
Organizing Your Code with Namespaces
A namespace is used to group code that is specific to a company, an application, or a given library. Namespaces help .NET programmers overcome naming conflicts for classes and methods. For instance, you cannot have two classes with the same name in the same namespace because it would confuse the .NET runtime and developers. Instead, your class names are unique inside your namespace.
You declare a namespace at the top of your code using the keyword namespace. Alternatively, you can set the default namespace inside your project properties. In this way, you do not have to see the outer namespace definition inside each code file. A common practice for defining namespaces includes using your company name followed by the application being written and then perhaps the library to which the code belongs. For example, you might define the namespace grouping for the Employee class as follows.
C#
namespace MyApplication.UserLibrary { public class Employee { } }
VB
Namespace MyApplication.UserLibrary Public Class Employee End Class End Namespace
You do not have to add this namespace information at the top of every code file in your project. This can become redundant and is error prone because a developer might forget to include the namespace definition. As an alternative, you can set the root namespace for your entire project using the project properties window. (Right-click the project file and choose Properties.) Figure 3.1 shows an example. This is a similar experience in both C# and VB. Note that you can define a root namespace here and still add additional namespace groupings in your code as necessary. Of course, those additional namespace definitions fall inside the root namespace.
FIGURE 3.1 Use the application properties to set the root namespace at the project level.
You access code inside a namespace by using the fully qualified definition of the namespace. For example, the .NET root namespace is System. If you were to access the String class, you would do so by using System.String. This is true for your code, too. To access the GetVacationUsed method, you might call out as follows.
MyCompany.MyApplication.UserLibrary.Employee emp = new MyCompany.MyApplication.UserLibrary.Employee(); short usedVaca = emp.GetVacationUsed();
As you can see, accessing code using the fully qualified namespace can be cumbersome in terms of typing and reading your code. Thankfully, you can import (with the using statement in C#) a namespace inside your code. This frees you from having to fully qualify each type you use. Instead, the compiler resolves class names based on your imported namespaces. Of course, the namespaces themselves are still required to prevent ambiguity in the compiler. Importing namespaces also help trim IntelliSense to those imported libraries.
In most cases, you do not get conflicts with imported namespaces. Type names are typically different enough in a given library that they do not overlap. If names do overlap, you can add qualification to eliminate the conflict.
You import namespaces using the using statement (C#) or Imports (VB) keyword. For example, the following shows namespaces imported into a class file for a Windows Forms application. The code includes the import statements for referencing the Employee class library.
C#
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using MyCompany.MyApplication.UserLibrary; namespace TestHarnessCSharp { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { Employee emp = new Employee(); //do work } } }
VB
Imports MyCompany.MyApplication.UserLibrary Public Class Form1 Private Sub Form1_Load(sender As Object, e As EventArgs) _ Handles MyBase.Load Dim emp As New Employee() 'do work ... End Sub End Class
Notice in the preceding example that the C# code has a number of additional using (or imports) statements at the top of the file. This is because VB files automatically import many of the default namespaces in .NET. The latest version of the IDE indicates which using statements are not necessary by fading their color. The IDE also provides a quick action to remove these if you want. Figure 3.2 shows an example.
FIGURE 3.2 You can easily eliminate unnecessary using statements in your code.
Types, Variables, and Constants
All classes, structures, and interfaces you create in .NET are considered types. That is, they define a specific type of data. The underlying .NET Framework Base Class Library also provides strong types. In fact, the .NET languages of both C# and VB are based on strongly typed objects. This means when you define a variable you create an instance of a strongly typed class. The .NET runtime can then rely on this type information for handling casting, comparisons, and other rules.
Data Types
A number of built-in types (classes) are used for common programming needs. These built-in types are referred to as data types and represent things such as a string of characters or a numeric value. You work with these data types like you would any structure or class. You can declare a variable of a certain type, create a new instance, or execute a method off of the type.
Most of the simple data types you use are value types (structures). There are a couple simple data types that are reference types (classes). These are string (System.String) and object (System.Object). Recall that value types store data (and copies of data) and that reference types instances store a reference to underlying data: this is important in terms of performance (cost of duplication with value types instances and cost of garbage collections induced by the reference type instances). Table 3.2 lists many of the common value types used in .NET programming; there are more than what is in this list. The list shows the underlying .NET Framework class, the range allowed in the data type, and the C# and VB data type names.
TABLE 3.2 Value Data Types by Language
.NET Framework |
C# Data Type |
VB Data Type |
Range |
System.Byte |
byte |
Byte |
0 to 255 |
System.Int16 |
short |
Short |
↦32,768 to 32,767 |
System.Int32 |
int |
Integer |
↦2,147,483,648 to 2,147,483,647 |
System.Int64 |
long |
Long |
↦9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 |
System.Single |
float |
Single |
±1.5 · 10↦45 to ±3.4 · 1038 |
System.Double |
double |
Double |
±5.0 · 10↦324 to ±1.7 · 10308 |
System.Decimal |
decimal |
Decimal |
±1.0 · 10↦28 to ±7.9 · 1028 |
System.Boolean |
bool |
Boolean |
true or false |
Many of the data types listed in Table 3.2 include unsigned versions. These are preceded with a u (as in ulong and uint). The System.Byte data type is the exception. It is unsigned. The signed version is called sbyte. Signed values include both the negative and the positive numbers in their range. Unsigned value types start at zero and include only positive numeric values.
Declaring Variables
When you declare a variable using a simple type, you typically want to declare the variable using the type that represents the lowest possible range for your situation. For example, if you were to define a variable to hold the month value, you might use System.Byte. If you were to define the year, you might use System.Int16. In this way, the lowest possible memory overhead is used for these types.
You declare a variable in C# by preceding the name of the variable with its type. In Visual, you use the Dim statement. The type then comes after the variable’s name. The following code shows an example of declaring variables in each language.
C#
byte month; short year; float paycheckAmount;
VB
Dim month As Byte Dim year As Short Dim paycheckAmount As Single
Of course, you can also define variables of other (more complex) types in the .NET Framework or types defined in your own class libraries. When you declare a variable, you can also assign it a default value or instantiate a new instance. The following shows an example.
C#
byte month = 1; short year = 2015; float paycheckAmount = 0; string name = "test"; Employee emp = new Employee();
VB
Dim month As Byte = 1 Dim year As Short = 2015 Dim paycheckAmount As Single = 0 Dim name As String = "test" Dim emp As Employee = New Employee()
Type Conversion
Again, both VB and C# are strongly typed languages. Therefore, the variables you declare cannot be reused by assigning different type values. Rather, they must always represent the underlying type to which they were declared or a type deriving from the variable type. This can be problematic. Sometimes, for instance, you have an integer that you need to pass to a method that only accepts a string. Or you need to parse a string value into an integer for a calculation. These are all instances in which you need to convert one type to another.
There are two conversions that you can make: implicit and explicit. An implicit conversion happens when you pass a smaller value type into a larger type that can contain the smaller value. In this case, if no data is lost, the conversion is allowed. For example, you can pass a short into a long without issue. However, passing a float (or double) into an integer might result in data loss and is thus not allowed as an implicit conversion; you need to explicitly convert. For example, the following code converts an integer value to a double. This code does not throw a type conversion error. Rather, it converts using implicit conversion.
C#
int intVal = 100; double doubleVal = intVal;
VB
Dim intVal As Integer = 100 Dim doubleVal As Double = intVal
If there is a chance that the conversion results in data loss, you must explicitly indicate your intention to convert types. This is called casting. You can also cast values that might otherwise be implicitly converted. In fact, this often makes your code more readable.
In C#, you cast a variable to another type by putting the type to which you are casting in parentheses in front of the type (or value) being cast, as in the following.
C#
double doubleVal = 12.345; int intVal = (int)doubleVal;
In Visual Basic, you cast a variable to another type using conversion keywords. These keywords have a C (for cast) in front of them followed by the type to which you are casting. For example, you can cast to an integer using CInt, a double using CDbl, or a string using CStr. The following shows an example.
VB
Dim doubleVal As Double = 12.345 Dim intVal As Integer = CInt(doubleVal)
There are times when you have a string value and need to convert it into a numeric. This cast is not allowed. However, most of the numeric types include the method Parse that enables you to parse a string into a numeric value. There is also TryParse, which returns a Boolean indicating whether the parse will work. It is recommended to use the latter for performance sake because with Parse, an exception is thrown if the parsing fails. The following code shows an example.
C#
string stringVal = "1234"; int intVal; intVal = int.Parse(stringVal);
VB
Dim stringVal As String = "1234" Dim intVal As Integer intVal = Integer.Parse(stringVal)
The framework also includes the Convert class, which enables you to convert one type to almost any other (including strings). This class is available to both Visual Basic and C# programmers.
Defining Constants
You might need to define values in your application that will not (and cannot) change during the execution of the application. In this case, you need to declare a constant. A constant in .NET is said to be an immutable value. That is, a constant cannot change values. You declare a constant in your code (typically at the class level) using the keyword const. Like a field, a constant can be private or public. The following shows an example.
C#
private const int CompanyTaxNumber = 123456;
VB
Private Const CompanyTaxNumber As Integer = 123456
Understanding Operators
Operators are indicators in your code that express an operation to perform. An operator might be an assignment from one variable to another, a comparison between two values, or a mathematical calculation among values. There are many operators available to .NET programmers. We do not cover them all here, but many of the more common operators are discussed in the following sections.
Assignment
Assignment operators are used to assign one variable or value to another. The most simple example is the equal (=) operator. This simply assigns the value on the right of the operator to the variable on the left side of the assignment (as in x = y). Other operators enable you to do assignment with addition (+=), assignment with subtraction (-=), assignment with multiplication (*=), and assignment of a string value with concatenation (&=). There are also assignment operators for division, arithmetic shifting, and more. The following shows a few assignment code examples.
C#
public double CalculatePaycheck(double gross, double commission, double deductions) { double paycheck = gross; //define paycheck as gross pay paycheck += commission; //add commission paycheck -= deductions; //subtract deductions return paycheck; }
VB
Public Function CalculatePaycheck(ByVal gross As Double, _ ByVal commission As Double, ByVal deductions As Double) Dim paycheck As Double = gross 'define paycheck as gross pay paycheck += commission 'add commission paycheck -= deductions 'subtract deductions Return paycheck End Function
Arithmetic
The arithmetic operations enable you to perform calculations on variables and using values. For example, you can use the multiplication operator (*) to multiply two numbers (x * y). All the operators you expect are available, such as addition (+), subtraction (-), division to return an integer (\), division to return a floating point (/), multiplication (*), and dividing for remainder (mod in VB, % in C#). There are other less common operators, too.
You typically use assignment with arithmetic operators, as in x = y * z. However, you can use the value of the calculation when making decisions in your code (without first assigning it to a variable); there’s more on this in the coming sections. As an example of basic arithmetic in code with assignment, the following code shows how you might calculate an employee’s accrued vacation days at any given point in the year. (The AccrualRate is either a constant or a set based on the number of days of vacation an employee has.)
C#
double accruedVacation = DateTime.Today.DayOfYear * AccrualRate;
VB
Dim accruedVacation as Double = DateTime.Today.DayOfYear * AccrualRate
Comparison
The comparison operators enable you to determine whether values are equal to, greater than, or less than one another. You typically use these operators comparing two variables, values, or expressions. The results of the comparison indicate whether or not (true or false) the comparison is valid (as in is x > y). The comparison operators include less than (<), less than or equal to (<=), greater than (>), greater than or equal to (>=), equal (= in VB and == in C#), and does not equal (<> in VB and != in C#). The following shows an example of assigning a variable of type Boolean to a comparison result.
C#
bool check = accruedVacation > vacationTakenToDate;
VB
Dim check As Boolean = accruedVacation > vacationTakenToDate
You can also do type comparison to check whether two objects point to the same reference (or not). In C#, this type of comparison is still done with the equal (==) and not equal (!=) operators. In Visual Basic, you use the keywords Is and IsNot, as in check = Employee1 Is Employee2.
Concatenation
The concatenation operations enable you to combine string values. In Visual Basic, the concatenation operator is an ampersand (&) sign used with two string variables or values. In C#, the plus (+) sign is used. Note that for performance sake, it is recommended to call string.Format or use a StringBuilder if you need to create large strings. The following shows an example.
C#
string fullName = firstName + " " + lastName;
VB
Dim fullName as String = firstName & " " & lastName
Logical and Conditional
The logical and conditional operators enable you to combine comparisons in different ways to help make decisions in your code. (See the next section for even more details.) For example, you might combine two comparisons to make sure they are both true. Alternatively, you might need to determine if at least one of the two comparisons is true. You can do this and more with the logical operators. Table 3.3 lists many of the logical operators. (For code examples, see the next section.)
TABLE 3.3 Logical and Conditional Comparison Operators
Purpose |
C# |
VB |
Pseudo Code Example |
Join two Boolean expressions and get the result, as in the result is true if both this and that are true. Note that both operands are evaluated. |
& |
And |
VB: check = (x>Y) And (x>0) C#: check = (x>Y) & (x>0) |
Negate a Boolean value or expression, as in the results equal the opposite of an evaluation. |
! |
Not |
VB: check = Not someVal C#: check = !someVal |
Choose between one or another values, as in the result is true if this or that is true. Note that both operands are evaluated. |
| |
Or |
VB: check = (x>y) Or (x>0) C#: check = (x>y) | (x>0) |
Two values must evaluate to opposite values, as in the result is true if this is true and that is false. |
^ |
Xor |
VB: check = True Xor False C#: check = true ^ false |
A short-circuited version of And in which, if the first condition does not pass evaluation, the second condition is not executed. |
&& |
AndAlso |
VB: check = (x>Y) AndAlso (x>0) C#: check = (x>Y) && (x>0) |
A short-circuited version of Or in which, if the first condition does not pass evaluation, the second condition is not executed. |
|| |
OrElse |
VB: check = (x>y) OrElse (x>0) C#: check = (x>y) || (x>0) |
Making Decisions and Branching Code
You can use the operators discussed previously to test for specific conditions in your code. These tests are then evaluated so you can make a decision on what code to execute or where to branch off in your application. There are three primary decision structures in .NET programming: If...Then...Else, Select...Case, and Try...Catch...Finally (as covered in the “Exception Handling” section later in this chapter).
If...Then...Else
You can use the If syntax in your code to test one or more conditions. Based on the results of your test, you might decide to execute one set of code if the condition proves true and another set of code if the condition proves false. You can also get into more complex scenarios by nesting If statements and using the logical operators discussed in the prior section.
In Visual Basic, you use the explicit If...Then statements nested with End If. In C#, you put your if conditions inside parentheses and the statements nested inside brackets. For example, the following shows code to determine whether an employee can get her vacation request approved. In this code, there is a nested if statement and an example of combining two conditions with and.
C#
public bool CanApproveVacationRequest(int daysRequested, int daysTaken, int daysAllowed, int daysAccruedToDate) { //rule: employee can take vacation if it is accrued and not used if ((daysRequested < daysAllowed) && (daysTaken < daysAllowed)) { if ((daysTaken + daysRequested) < daysAccruedToDate) { return true; } else { return false; } } else { return false; } }
VB
Public Function CanApproveVacationRequest(ByVal daysRequested As Integer, ByVal daysTaken As Integer, ByVal daysAllowed As Integer, ByVal daysAccruedToDate As Integer) As Boolean 'rule: employee can take vacation if it is accrued and not used If daysRequested < daysAllowed And daysTaken < daysAllowed Then If (daysTaken + daysRequested) < daysAccruedToDate Then Return True Else Return False End If Else Return False End If End Function
Note that in Visual Basic if you have a single line that executes based on an if condition you can write that as a single line of code, as in If x > 500 Then doSomething. In C#, if you have a single line that executes, you can eliminate the need for the braces, and the statement following the if condition is executed based on the condition’s evaluation.
Select...Case (Switch)
The Select...Case (switch in C#) code construct enables you to evaluate a single statement for a value. Based on this condition, you then can execute blocks of code depending on the value.
In C#, you define the condition inside parentheses following the keyword switch. You then define each case block with the keyword case, the value you are checking on, and a colon. You must then add a break statement at the end of the case to indicate the end of the case. You can use the default keyword to execute code if no case was realized. The following code shows an example.
C#
private void CalculateAdditionalCompensation() { switch (this.Status) { case EmploymentStatus.Contract: //code for contract employees break; case EmploymentStatus.Hourly: //code for hourly employees break; case EmploymentStatus.Salaried: //code for salaried employees break; case EmploymentStatus.SalariedCommissioned: //code for commissioned employees break; case EmploymentStatus.Other: //code for other employees break; default: //code that runs if bad status was set break; } }
In Visual Basic, you write case Select...Case statements using the keyword Select followed by Case followed by the condition. Each condition is then preceded with Case. You can use Case Else to run code when no other condition value evaluates. Here is a code example.
VB
Private Sub CalculateAdditionalCompensation() Select Case Me.Status Case EmploymentStatus.Contract 'code for contract employees Case EmploymentStatus.Hourly 'code for hourly employees Case EmploymentStatus.Salaried 'code for salaried employees Case EmploymentStatus.SalariedCommissioned 'code for commissioned employees Case EmploymentStatus.Other 'code for other employees Case Else 'code that runs if bad status was set End Select End Sub
Looping
There are many times in your code when you need to execute a set of statements more than once. In these cases, you need to create a loop. The most common scenarios are looping through code a set number of times, looping until a condition becomes true or false, or looping through code once per element in a collection of objects. (See the section “Working with Groups of Items” later in this chapter.)
For...Next
The For...Next construct enables you to execute a block of code statements a set number of times. This is accomplished through a counter that increments a set number of steps each time the loop executes. After the counter has reached a max value, the looping completes.
In C#, you write a for statement inside parentheses. The for statement has three parts: counter declaration, condition for the counter, and counting step. Each part is separated by a semicolon. The following code shows an example of executing a code block once for each employee’s direct report.
C#
for (int i = 0; i < numDirectReports; i++) { //update employee based on num of direct report }
In Visual Basic, your For statement is a little more readable. You indicate the counter, the initial value, and the To value. Optionally, you can add the Step keyword to indicate how many times you want to increment the counter each time through the loop. Here is a code example:
VB
For i As Integer = 1 To numDirectReports 'update employee based on num of direct reports Next
For...Each (Iterators)
Like For...Next, the For...Each construct enables you to execute a group of statements. However, For...Each executes once for each element in a group of elements (or a collection). For instance, if you add a block of code to the Employee class that needs to execute once for each DirectReport, you could do so using the For...Next (as shown previously) and then execute based on the count of DirectReports. However, using For...Each allows you to iterate over each object in a collection. As you do, you get a reference to the given object that you can use in your code. This makes coding a little easier to write and to understand.
You implement For...Each similar to For...Next in both C# and Visual Basic. The following shows code that executes once for each Employee instance inside the collection DirectReports.
C#
foreach (Employee emp in DirectReports) { //execute code based on each direct report // using the item as in emp.Name }
VB
For Each emp As Employee In DirectReports 'execute code based on each direct report ' using the item as in emp.Name Next
Do...While/Until
Sometimes you need to repeat a block of code as many times as required until a condition evaluates to true or false. You might be looking for a specific value or might be using a counter that increments based on logic (instead of standard steps). In these cases, you can use a Do...While or a While loop. A Do...While loop executes once before the condition is evaluated to determine whether it should execute a second time. A While loop evaluates the condition first and then only executes if the condition evaluates to true.
In C#, you can create Do...While loops using the do keyword followed by your block of code in braces. The while statement is written at the end of the code block indicating that the statements are executed once before looping. (Use a while loop to evaluate a condition before looping.) The following shows an example.
C#
do { //get next project and calculate commission projectCommission = GetNextProjectCommision(empId); calculatedCommission += projectCommission; if (projectCommission == 0) break; } while (calculatedCommission < MaxMonthlyCommission);
Notice in this code the use of the break keyword. This indicates that the code should break out of the Do...While loop. You can also use the continue keyword to skip remaining code in your code block and jump right to the while statement to force a condition evaluation (and possible another loop).
In Visual Basic, you can define the While (or until) statement at the top or bottom of the loop. If defined at the top, your statement is evaluated before the loop executes once. If at the bottom, the loop executes at least once before the statement is evaluated. The While keyword indicates that you want to loop while a condition is true (until it becomes false). The Until keyword allows you to loop until a condition evaluates to true (while it is false). The following shows an example.
VB
Do 'get next project and calculate commission projectCommission = GetNextProjectCommision(empId) calculatedCommission += projectCommission If projectCommission = 0 Then Exit Do Loop While calculatedCommission < MaxMonthlyCommission
As mentioned before, there is also the basic While loop (without do). This simply loops a block of code while a condition evaluates to true. Also, like all looping constructs, you can nest Do...While loops to handle more complex situations.
Working with Groups of Items
A common scenario in computer programming is managing a group of similar items. For example, you might need to work with a set of values, such as ZIP Codes to which a sales representative is assigned. Alternatively, you might need to work with a group of objects such as the paychecks an employee has received in a given year. When you need to work with a group of elements, you can do so using an array or a collection class. The former is great for working with a set sequential list of items of the same type. The latter is more applicable for managing a variable-sized group of objects.
Arrays
An array is a group of items of the same type (either value or reference types). For instance, you might create an array that contains all integer values or all string values. You also have to define the number of elements contained in your array when you first initialize it. There are ways to expand or contract this size, but these typically involve copying the array into another array. If you need the flexibility of adding and removing items in a group, you want to use a collection class and not an array.
When you define an array’s size, you need to know that they are zero-based arrays. That is, the first element in the array is item zero. Each item is contiguous and sequential. This enables you to set and access items quickly using the items index.
The typical array you create is one dimensional, meaning that it contains a single group of indexed items. You declare this type of an array by indicating the number of elements in the array either on the declaration of the variable or before the array’s first use. There are a few valid syntaxes for defining an array. The standard way in C# is to use the new keyword to set the size of the array. In Visual Basic, you can set the size of the array without using the keyword new. The following shows an example.
C#
short[] salesRegionCodes = new short[numRegions];
VB
Dim salesRegionCodes(numRegions) As Short
You access an array through its index value. Array objects inherit for the System.Array class. This gives you a number of properties and methods you can use, including getting the total number of elements in all dimensions of an array (Length) and getting the upper-bound value for a single dimension (GetUpperBound). The following code shows an example of using this last method and accessing an array through its index.
C#
for (int i = 0; i < salesRegionCodes.GetUpperBound(0); i++) { short code = salesRegionCodes[i]; //additional processing ... }
VB
For i = 0 To salesRegionCodes.GetUpperBound(0) Dim code As Short = salesRegionCodes(i) 'additional processing ... Next
You can also initialize the values in an array inside the declaration statement. In this case, the number of elements you define sets the size of the array. The following is an example.
C#
double[] salesFigures = new double[] {12345.98, 236789.86, 67854.12};
VB
Dim salesFigures() As Double = {12345.98, 236789.86, 67854.12}
You can define arrays that have more than a single dimension (up to 32). A common scenario is a two-dimensional array in which one dimension is considered rows and the other columns. You can use the Rank property to determine the number of dimensions in an array.
For an example of a multidimensional array, consider one that contains sales figures for each sales representative (rows) in each region (columns). You might define this array as follows.
C#
double[,] salesByRegion = new double[6, 5];
VB
Dim salesByRegion(6, 5) As Double
Note that an array can also contain other arrays. These type of arrays are called jagged arrays (or arrays of arrays). They are considered jagged because each element in the array might contain an array of different size and dimension; therefore, there might be no real uniformity to the array.
Collection Classes and Generics
A collection class can give you more flexibility when working with objects. For example, you can have objects of different types in a single collection; collections can be of varying lengths; and you can easily add and remove items in a collection.
The standard collection classes are defined inside the System.Collections namespace. The classes in this namespace include a base class for creating your own, custom collections (CollectionBase) and more specific collections such as ArrayList, Stack, SortedList, Queue, and HashTable.
For example, you might create a simple, dynamic ArrayList to contain a set of sales figures. The following code shows how you can create a new ArrayList, add items to it, and loop through those items.
C#
ArrayList salesFigures = new ArrayList(); salesFigures.Add(12345.67); salesFigures.Add(3424.97); salesFigures.Add("None"); for (int i = 0; i < salesFigures.Count; i++) { object figure = salesFigures[i]; //process figures ... }
VB
Dim salesFigures As New ArrayList() salesFigures.Add(12345.67) salesFigures.Add(3424.97) salesFigures.Add("None") For i As Integer = 0 To salesFigures.Count - 1 Dim figure As Object = salesFigures(i) 'process sales figure data ... Next
Of course, many additional properties and methods are available to you through the ArrayList and related collection classes. You should explore these for your specific scenarios.
Notice in the preceding code that the collection class has two types of objects inside it: double and string. This can be problematic if you need to rely on a collection of objects all being of the same type. For example, you might want all your sales figures to be of type double; or you might want a collection of only Employee objects. In these cases, you need a strongly typed collection class. You can create these by coding your own, custom collection classes (inheriting from CollectionBase and implementing the interfaces specific to your needs). However, .NET also provides a set of classes called generics that allow for strongly typed groups of objects.
Generic collections can be found inside the System.Collections.Generic namespace. A generic collection class enables you to define the type that the class contains when you initialize it. This then restricts what types the class can contain. You can rely on this information within your code.
You define a generic list in C# using angle brackets (<>) with the type defined inside those brackets. In Visual Basic, you define the generic type inside parenthesis using the Of keyword. For example, the following defines a simple, generic list of items that can only include values of type double.
C#
List<double> salesFigures = new List<double>();
VB
Dim salesFigures As New List(Of Double)
There are many generic collection classes available to you, including Dictionary, HashSet, LinkedList, List, Queue, SortedList, Stack, and more. You can also write your own generic collection classes.
Tuple
The System.Tuple class enables you to create a set, ordered list of items and work with that list. After you’ve created the list, you cannot change it. This makes for easy storage (and access) of sequential items.
For example, if you wanted to create a Tuple to store the month names in the first quarter, you could do so using the static member Tuple.Create. Each item you want to add to the list you add inside parentheses (and separated by commas). You can then access the items in your Tuple using the Item1, Item2, Item3 syntax. Note that the Tuple only exposes item properties for the number of items that exist inside the group. The following code shows an example.
C#
var q1Months = Tuple.Create("Jan", "Feb", "Mar"); string month1 = q1Months.Item1;
VB
Dim q1Months = Tuple.Create("Jan", "Feb", "Mar") Dim month1 As String = q1Months.Item1
The Tuple class is based on generics. You define the type of object you enable for each member in the list. The Create method shown infers this type for you. However, you might want to be explicit. In this case, you can declare your types using the constructor as follows.
C#
Tuple<int, string, int, string, int, string> q1MonthNumAndName = Tuple.Create(1, "Jan", 2, "Feb", 3, "Mar");
VB
Dim q1MonthNumAndName As Tuple(Of Integer, String, Integer, String, Integer, String) = Tuple.Create(1, "Jan", 2, "Feb", 3, "Mar")
Programming with Attributes
Sometimes you need to provide metadata about the capabilities of your code. This metadata is meant to tell other code that is inspecting your code (through reflection) specific things about what the code might do. This includes information for the .NET runtime, such as how you want your code compiled. There are many attributes available in the .NET Framework. You can also create your own, custom attributes to be applied to your code. In this case, you can write code to examine the metadata about your own application.
Declarative attributes can be applied to classes, properties, methods, parameters, and other elements inside your code. You can apply a single attribute or multiple attributes to an application. Some attributes also might take parameters to indicate additional information to the attribute code.
Note that, by convention, all attributes end with the word Attribute in their names, such as SerializableAttribute. You typically leave the word attribute off your declaration, however, because it is not required.
In C#, attributes are placed on code using square brackets ([]). For example, you can use the ConditionalAttribute to indicate to the compiler which code should be compiled based on environment variables or command-line options. You would apply this attribute to your code as shown.
C#
[System.Diagnostics.Conditional("DEBUG")] public void EmployeeCalculationsTestMethod() { //code that compiles in the debug version of the assembly }
In Visual Basic, you decorate your code elements with an attribute by putting the attribute in angle brackets (<>) in front of the code element, as follows.
VB
<Conditional("DEBUG")> Public Sub EmployeeCalculationsTestMethod() 'code that compiles in the debug version of the assembly End Sub
Exception Handling
A lot of programming time is spent eliminating exceptions from our code. However, you can’t always eliminate all scenarios that might cause an exception. In these cases, you need a way to anticipate the exception and then, if possible, handle the exception in your code. There is where the Try...Catch...Finally construct comes into play.
You put a Try statement around a block of code you expect might cause an exception. You typically do so if you intend to handle the error. If you are not intending to handle the error, you can let the error bubble up to the calling code. Of course, you need to have an outer-error handler (or manager) inside your outer code to prevent errors from bubbling up to users in nasty ways.
When an exception actually occurs inside your Try block, execution is immediately passed to a Catch block. This might be a general catch of all errors or a catch meant for a specific exception type. The code inside the catch block is then meant to handle the error. Handling an error might include logging the error, sending it to a message system, or actually trying something different (or trying again using a jump statement) as the result of the error.
The following shows a basic example. Inside the Try block is a calculation that does division. This Try block has the possibility of raising an exception in the case where the division is done by zero. This condition raises the specific exception DivideByZeroException. There is a Catch block that consumes this (and only this) type of exception. You can add code to the Catch block to either eat the exception (do nothing) or process it somehow. Also, if you want to rethrow the exception after handling it, you can do that, too.
C#
try { averageSales = salesToDate / avgRate; } catch (System.DivideByZeroException e) { //handle the exception ... // if rethrowing use: throw; }
VB
Try averageSales = salesToDate / avgRate Catch ex As System.DivideByZeroException 'handle the exception ... ' if rethrowing use: Throw End Try
You can have multiple Catch blocks that are both specific and generic. Note that if no exception type is found in a Catch block, the exception is actually not handled but is bubbled up to the calling code (or to the runtime).
Note that you can also rethrow the error from your Catch block using the Throw keyword. If you do not rethrow the exception, the runtime assumes you have handled the error and moves on. You can also use throw anywhere in your application where you want to raise an exception.
There is also a Finally block that you can write. This bit of code goes after your Catch blocks and runs regardless of whether an exception is raised. That is, it will always run after the code execution path exits the try block, either as a normal exit, after an exception is raised, or a call has been made to return. In all cases, the Finally block will execute. It is useful for cleaning up any resources that might have been allocated inside the Try block.
Creating and Raising Events
There is not much functionality you can build using the .NET languages without events. Events enable one piece of code to notify another bit of code that something has just happened. Code that raises events is said to publish an event, and code that receives the event notice is said to subscribe to events. A simple example is when you write a user interface for the Web or Windows. In these cases, you are consistently adding code that subscribes to events published by the UI, such as a user clicking a button control. Of course, an event may have more than a single subscriber, and subscribers may subscribe to multiple events.
Define an Event
When you define an event you need to determine whether you need to pass custom data to the subscribers. This custom data is referred to as event arguments (or args). If you do not need to pass custom data, you simply declare the event using the keyword event and the existing delegate EventHandler. For example, if you were to define a simple event that you would raise when an employee class is updated, you might define that event as follows.
C#
public event EventHandler EmployeeUpdatedEvent;
VB
Public Event EmployeeUpdatedEvent As EventHandler
By declaring the event, you have effectively published it. Subscribers who have a reference to your class can then set up a subscription to your event. You then need to raise the event in the same class where you published it. This notifies the subscribers that the event has fired.
It is slightly more complicated to define events where you need to pass custom data. In this case, you must first create a custom class to maintain your event data. This class must inherit from the EventArgs base class. For example, you might create a custom event arguments class to contain the employee ID for the employee-updated event. In this case, your custom class contains a property to hold the Id value and a constructor for passing in this value, as in the following code.
C#
public class EmployeeUpdatedEventArgs : EventArgs { public EmployeeUpdatedEventArgs(string id) { _id = id; } private string _id; public string EmployeeId { get { return _id; } } }
VB
Public Class EmployeeUpdatedEventArgs Inherits EventArgs Public Sub New(ByVal id As String) _id = id End Sub Private _id As String Public ReadOnly Property EmployeeId() As String Get Return _id End Get End Property End Class
When you use a custom event argument, you need to declare your event to use the custom event argument class. You can do so using the version of the EventHandler class that is defined as generic. In this case, you indicate the class that contains the argument as part of the generic definition of EventHandler. This class also automatically contains the sender argument (typically a copy of the object publishing the event). The following shows an example of defining this custom event handler.
C#
public event EventHandler<EmployeeUpdatedEventArgs> EmployeeUpdatedCustomEvent;
VB
Public Event EmployeeUpdatedCustomEvent As _ EventHandler(Of EmployeeUpdatedEventArgs)
Raise an Event
You raise the event in the same class where the event is defined. An event is raised as the result of some action. In the case of the example, the action in the employee class has been updated. To raise the event, you simply call it in the right spot and pass the appropriate parameters. In the case of the employee-updated custom event, you pass an instance of the employee class as the sender and then the employee Id as part of an instance of the EmployeeUpdatedEventArgs, as shown here.
C#
public void UpdateEmployee() { //do work to update employee ... //raise event to notify subscribers of the update EmployeeUpdatedCustomEvent(this, new EmployeeUpdatedEventArgs(this.Id)); }
VB
Public Sub UpdateEmployee() 'do work to update employee ... 'raise event to notify subscribers of update RaiseEvent EmployeeUpdatedCustomEvent(Me, _ New EmployeeUpdatedEventArgs(Me.Id)) End Sub
Subscribe to and Handle an Event
The final step is to actually listen for (or subscribe to) the event. Here, you need to do two things. First, you must write a method that mimics the signature of the event. The content of this method is yours to write. It is called when the event fires. The following shows an example of a method (inside a class that subscribes to the employee class) that is called when the event fires. Notice how this method uses the custom event type and must therefore match that signature.
C#
private void OnEmployeeUpdate(object sender, EmployeeUpdatedEventArgs e) { //do something in response to employee update string empId = e.EmployeeId; }
VB
Private Sub OnEmployeeUpdate(ByVal sender As Object, _ ByVal e As EmployeeUpdatedEventArgs) Dim empId As String = e.EmployeeId Console.WriteLine("Event Fired: id=" & empId) End Sub
Second, you must register your event handler with the actual event. You do this by adding a pointer to the event using the += (C#) or AddHandler (VB) syntax. You typically add your handlers inside the subscribing class’s constructor or initialization code. The following shows code to connect the OnEmployeeUpdate handler to the EmployeeUpdatedCustomEvent event.
C#
Employee _emp = new Employee(); _emp.EmployeeUpdatedCustomEvent += this.OnEmployeeUpdate;
VB
AddHandler _emp.EmployeeUpdatedCustomEvent, AddressOf OnEmployeeUpdate
When the code is run, you undoubtedly access features of the class that fire the event (in this case, Employee.UpdateEmployee). When you hit a method that triggers the event, your subscribing code is called accordingly. When you don’t need to listen to the event, you must remember to unsubscribe your handler from the event. If you forget to do so, the garbage collector might not be able to free the memory used by the listener object.