Home > Articles > Programming > Windows Programming

Understanding Delegates in Visual Basic .NET

This sample chapter from Visual Basic .NET Unleashed demonstrates the fundamentals of singlecast and multicast delegates. Delegates support a dynamic event programming model as well as passing procedural types as arguments.
This sample chapter is excerpted from Visual Basic .NET Unleashed, by Paul Kimmel (Sams Publishing, 2002, ISBN 0-672-32234-X).
This chapter is from the book

This chapter is from the book

Visual Basic 6 provided an opportunity for us to become familiar with events as a dynamic aspect of Windows programming. From Chapter 8, you know that an event is an occurrence in your program, and an event handler is a procedure defined to respond to that occurrence.

To review, the event-handling mechanism works because procedures are effectively addresses. If we know the arguments that are passed to a procedure and have a procedure's address, we can invoke a procedure because this is how procedure invocation works internally. VB6 allowed us to pass the address of a procedure to Windows for API calls that needed a callback address, but didn't support callbacks within VB6 itself. Visual Basic .NET supports procedural types through the Delegate class. Delegates maintain the addresses of procedures used as callback procedures. When referring to a procedural type, think of a variable declaration whose type happens to be the signature of a procedure. Procedural type is the generic term that has been in existence in other languages for years; in Visual Basic .NET, procedural types are specifically referred to as delegates.

Because delegates are classes in Visual Basic .NET, we have extended capabilities beyond one instance of a Delegate—procedural type—containing a single address of one procedure. Delegates are implemented to support a list of addresses referred to as an invocation list. Delegates that contain multiple procedure addresses are referred to as multicast delegates. Multicast delegates support a single event having multiple respondents.

NOTE

VB6 required a control array if you wanted one event handler to handle events for multiple controls. Visual Basic .NET introduces the Delegate class to keep track of event handlers. Visual Basic .NET supports multiple event handler respondents for a single control event and supports multiple controls being associated with a single event handler.

In Chapter 9, you will learn all about defining, declaring, and invoking delegates. Additionally, I will demonstrate how delegates can be used as procedure arguments to support dynamic behavior. We will begin coverage of delegates in this chapter by looking at one of the most common pre-existing delegates, EventHandler.

Using the EventHandler Delegate

The most common delegate is EventHandler. A delegate is defined by preceding the name and signature of a procedure with the keyword Delegate. Applying this to what we know about the EventHandler delegate, we see that in Visual Basic .NET we can write a statement similar to the following:

Delegate Sub EventHandler(sender As Object, e As System.EventArgs)

This statement identifies a type name EventHandler as a delegate that takes an Object and System.EventArgs parameters. (If you've written or seen a function pointer in C/C++ or defined a procedural type in Object Pascal, this syntax will appear similar to you.) The delegate EventHandler is a type. Variables of type EventHandler can be the AddressOf any subroutine that has the same signature as the EventHandler delegate; specifically, the address of any subroutine that takes an Object and System.EventArgs parameters, in that order, can be assigned to an instance of an EventHandler delegate. We will come back to defining delegates and declaring instances of delegates in upcoming sections. For now, because EventHandler is so prevalent, let's take a look at how we can employ its generic arguments.

TIP

Delegates can be initialized with subroutines or functions.

Using the EventHandler Object Argument

The generic signature of the EventHandler Delegate wasn't picked by accident. From other architectures, specifically Delphi, a common ancestry has proven to be effective in implementing event handlers for controls like buttons and forms.

Many controls, for example, support a click event. To respond to an event, it's often helpful to know the originator of the event. For example, when a button is clicked, it's often helpful to be able to use the button object itself. The same may be true for forms or pictureboxes; you may want to respond to a click event. Without a common ancestry, an event handler would have to be defined specifically for each of these controls. An event handler for a picturebox would take a PictureBox argument, a form handler a Form argument, and so on. All of these variations of event handlers would cause any implementation supporting dynamic event handlers to swell up and complicate using the event handlers.

Consider a better alternative. A click event really just needs the object that invoked the event. Assuming a common ancestry—which is what we have in Visual Basic .NET—we can define one type of event handler and allow polymorphism to support specific behaviors for subclasses of Object.

This is exactly what we have in Visual Basic .NET. Object is the common ancestor for classes, ValueTypes (like Integer), and structures. Roughly, this means that anything can be passed to satisfy the Object argument and dynamic type-checking through the TypeOf operator can be used to determine the specific subclass passed to satisfy that argument.

NOTE

Let's pause for a minute and examine the need for a generic object reference. It begs the question: If a generic object parameter is so important, why are we just now getting an implementation of event handling that supports it?

The direct answer is that previous versions of VB had some shortcomings. Delegates are one of the reasons Microsoft can market Visual Basic .NET as a first-class language. VB6 supported a weaker style of programming event handlers.

In VB6, we would implement an event handler and refer to the specific object in the event-handling code, for example:

Sub Command1_Click()
 MsgBox Command1.Name
End Sub

NOTE

Unfortunately, this style of programming tightly couples the event handler with a single control. In VB6, this worked moderately well, because in the absence of a control array only one control would be using this code. However, this code broke if you changed the name of the control. Visual Basic .NET supports the event handler as a property of the control. Consequently, if the control name changes, the property value doesn't, and the event handler still works correctly.

Additionally, Visual Basic .NET supports assigning multiple control events to a single handler. Thus the same handler may be invoked by many objects. Attempting the latter would break the VB6 model for event handlers. From the preceding fragment, the equivalent of Command1_Click may not have been invoked by the Command control.

Delegates and a stronger event programming model required that event handlers have arguments, and sanity justified a polymorphic means of implementing event handlers.

Another factor may be that Distinguished Architect Anders Hejlsberg was instrumental in implementing Delphi event handlers this way and Microsoft needed something that worked.

The benefit of a generically defined event-handling Delegate means that multiple controls, supporting semantically similar operations, can be assigned to exactly the same event handler without using control arrays or specific references to controls.

Multiple Event Respondents

Suppose we have a main form with two metaphors for closing the application. For argument's sake, suppose that a File, Exit menu closes the application by closing the main form, as does a button with the text Quit. Each metaphor for closing the application performs semantically the same operation—to run the End statement. Clearly, one procedure should be able to handle this operation no matter how it is invoked.

The solution EventHandlerDelegate.sln on this book's companion web site contains the code for this example. The form is implemented by adding a MainMenu control from the Windows Forms tab of the toolbox and a Button to a Form. Add a File menu with an Exit submenu by clicking and typing in the menu designer on the form (see Figure 1), and modify the Text property of Button, adding the text Quit.

Figure 1 MainMenu control editing can be performed directly on the Form Designer as shown.

Double-click on the Exit submenu to generate the event handler procedure body (shown in Listing 1). Add the Quit button to the Handles clause. In this example, the default control names were maintained, so you'll see Handles MenuItem2.Click, Button1.Click in the listing.

Listing 1—An Event Handler Responding to Similar Events for Two Separate Controls

Private Sub MenuItem2_Click(ByVal sender As System.Object, _
 ByVal e As System.EventArgs) Handles MenuItem2.Click, Button1.Click
  End
End Sub

The code is simple. It calls End to terminate the application. What is important is that MenuItem2 and Button1 are two disparate controls with semantically identical events, allowing one event handler to respond to an event raised by either control. Handles MenuItem2.Click and the event handler were added by double-clicking on the Exit menu item. The Button1.Click predicate was added manually.

In Listing 1, the code doesn't make use of either the Object or System.EventArgs argument. Sometimes, as in the example, you won't need these arguments. However, if the response is dependent on the type of the argument, you will need to type-check the sender argument to determine which action to take.

Type-Checking the Object Argument

Suppose in the example described in the previous section you wanted to perform one of two slightly different operations based on which metaphor was used to invoke the operation. For example, Quit might prompt the user to make sure he or she wanted to exit not only the current context but the entire program, whereas the more deliberate File, Exit might suggest that the user is clear about his or her intentions and definitely wants to exit the application.

We could perform dynamic type-checking on the sender argument in this instance, still using the same event handler and performing similar operations—quitting—but the Quit menu would ask the user to verify his or her intentions. You can type-check the sender argument by using the TypeOf operator.

From the implementation statement, after the user chooses the Quit button, the application exits if the user responds affirmatively to a verification prompt; otherwise, the application does not end. Listing 2 demonstrates the revision to Listing 1.

Listing 2—Revised Code from Listing 1, Which Dynamically Type-Checks the sender Argument

 1: Private Sub MenuItem2_Click(ByVal sender As System.Object, _
 2:  ByVal e As System.EventArgs) Handles MenuItem2.Click, Button1.Click
 3:
 4:  If (TypeOf sender Is Button) Then
 5:   If (MsgBox("Are you sure?", MsgBoxStyle.YesNo, "Exit Application") _
 6:    = MsgBoxResult.No) Then
 7:
 8:    Exit Sub
 9:
10:   End If
11:  End If
12:
13:  End
14:
15: End Sub

The revised code is defined on lines 4 through 11. Lines 4 through 11 test the negative case. If sender is a button and the user answers No to the MsgBox prompt, the subroutine exits; otherwise, the application is terminated on line 13. The type of the sender argument is checked on line 4 with the TypeOf operator. In the next subsection, we will use this operator to determine the type of the sender argument and cast the Object type to a specific type.

Refactoring and Algorithmic Decomposition

Let's take a moment to address stylistic issues. Some programmers may object to multiple exit points, but other than some programmers finding the code a little confusing, there is no prohibition against multiple exit points. The alternative is to test the positive case:

If sender is button type then
 If user wants to quit is True then
  End
 else (user doesn't want to quit) Exit Sub
else (sender is menu item) End

Testing the positive case results in needing the End statement to appear twice.

As a matter of taste, I would prefer one exit point and a simpler event handler. In this event handler, all I want to know is whether the application can terminate. How termination is decided adds too much complexity to the event handler. Essentially the algorithm is "If CanQuit is True Then End" and this reflects what the code should say. Listing 3 demonstrates the revision using the more precise version of the algorithm.

Listing 3—A More Precise Implementation of the Dual-Metaphor Application Termination Event Handler

1: Private Sub MenuItem2_Click(ByVal sender As System.Object, _
2:  ByVal e As System.EventArgs) Handles MenuItem2.Click, Button1.Click
3:
4:   If (CanQuit(sender)) Then End
5:
6: End Sub

At the point this event handler is run, and the code now only asks "Can I quit?" If the answer is yes, the application terminates. (This is the singular level of complexity I strive for in my code.) Now the only problem is to implement CanQuit. Just as the event handler is a singular procedure now, so will CanQuit be. This singular division of labor plays to the short-term memory and problem-solving capability of the human mind and is supported by the concept of refactoring.

To implement the revision to Listing 3 from Listing 2, the refactoring "Extract Method" (introduced in Chapter 8) can be used twice to factor the prompt "Are you sure?" and the test to determine whether sender is a button. Alternatively, you can decompose the problem as an algorithm and then implement each of the supporting pieces of the algorithm. CanQuit is decomposed as "sender is a button and prompt response is yes" or "sender isn't a button."

Choosing refactoring or algorithmic decomposition depends on where you are in development of the code. If the code is already written, use the refactoring; if you are writing the code for the first time, state the algorithm and decompose it into its supporting pieces. Refactoring implies revision after the fact, versus decomposition, which is revision before the fact. Because the long version was introduced first, I will demonstrate the Extract Method refactorings in the bulleted list that follows. (Use Listing 4 to follow along with the bulleted list of steps.)

  • Factor out the MsgBox statement to a Function Quit that displays the message box prompt. Quit returns True if the user clicks Yes in response to the message box.

  • Replace the literal call to MsgBox in Listing 2 with the call to Quit() testing for False; that is, Quit() = False.

  • Replace the dynamic type-check of sender with a function IsButton taking a sender As Object argument. (I am performing this refactoring for clarity here, but probably wouldn't in production code.)

  • Replace the dynamic type-check with the call to IsButton, passing sender in Listing 2. If codified, this change would yield the following:

  • If( IsButton(sender)) Then
     If( Quit() = False ) Then
      Exit Sub
     End If
    End If
    End
  • Define a Function CanQuit(), which returns a Boolean. Perform the positive test in CanQuit to return a Boolean True if sender is a button and the response to Quit is True or sender isn't a button. CanQuit is implemented using the IsButton and the Quit methods defined thus far. CanQuit() is shown in Listing 4.

Listing 4—Revision of the Code from Listing 3 Using the Refactoring Extract Method

 1: Private Function Quit() As Boolean
 2:  Const Prompt As String = "Are you sure?"
 3:  Return MsgBox(Prompt, MsgBoxStyle.YesNo,
 4:  "Exit Application") = MsgBoxResult.Yes
 5: End Function
 6:
 7: Private Function IsButton(ByVal sender As Object) As Boolean
 8:  Return TypeOf sender Is Button
 9: End Function
10:
11: Private Function CanQuit(ByVal sender As Object) As Boolean
12:  Return (IsButton(sender) AndAlso Quit()) Or Not IsButton(sender)
13: End Function
14:
15: Private Sub MenuItem2_Click(ByVal sender As System.Object, _
16:  ByVal e As System.EventArgs) Handles MenuItem2.Click, Button1.Click
17:
18:   If (CanQuit(sender)) Then End
19:
20: End Sub

Listing 4 is longer than Listing 2, the original implementation. In fact, refactoring may result in temporarily longer fragments of code but shorter, more reusable algorithms and fewer lines of code in an overall system. Each algorithm in Listing 4 is expressive and very easy to understand.

NOTE

I have met many people who don't understand the style of code in Listing 4. Simplistically, it seems as if I have traded one longer procedure for many shorter ones. A counter argument on the benefit side is that whereas the reader had to remember one slightly longer procedure, now the reader has to remember several, although short, procedures.

In very short examples, the use of many short procedures to replace a few longer ones seems to make very little sense. Instead of remembering what lines do, you have to figure out what functions do. Keep in mind that this argument only makes sense in individual examples, not systems. Using the strategies—decomposition or refactoring—discussed in this section results in more legible code, the need for fewer comments, procedures that are easier to understand and debug, and a greater number of reusable procedures. Time and experience bear out these assertions and the adoption of refactoring as a methodology supports the argument for singular, factored procedures.

Admittedly none of the methods in Listing 4 can be used again, but it's the overall strategy of factoring code to make individual pieces very easy to understand and more likely to be reused that we are striving for. Further, we are unlikely to know what a candidate for reuse is at the moment we are implementing a particular procedure. Consequently, refactoring provides us with an avenue for extracting code when potential reuse is identified.

NOTE

The preceding paragraph referring to reusable code is our justification for an architectural model. Without models it becomes increasingly difficult to realize optimal code reuse because developers lose track of available classes and procedures.

Perhaps the absence of models is the reason the industry is not realizing the full potential of object-oriented development. (The last statement is based on personal experience. Only one in 30 projects that I have worked on was actively building an architectural model prior to my participation.)

Typecasting the Object Argument

If you use the sender argument of an EventHandler Delegate as is, you will only be able to use the members of the Object class. To use members of the specific instance, you will need to determine the actual type of the sender argument and cast sender to that type. The preceding section demonstrated the TypeOf and Is operators. To cast a base class to a specific subclass, use the CType function.

Alan Cooper, in his book The Inmates Are Running the Asylum (Sams, 1999, ISBN 0-672-31649-8), addresses confirmation dialog boxes in the opening sentence of the chapter "Software Won't Take Responsibility." "Confirmation dialog boxes are one of the most ubiquitous examples of bad design; the ones that ask us 'are we sure' that we want to take some action." (p. 67) Cooper goes on to suggest that the "are you sure" dialog box was designed to absolve the programmer of responsibility. Instead of prompting "are you sure," Cooper suggests that the user should be presumed sure but able to change her mind later, and that it's the programmer's responsibility to make sure that the user can change her mind (that is, undo an action).

NOTE

Alan Cooper is the original inventor of Visual Basic, although he hasn't been active in its implementation or design for many years. When I asked Mr. Cooper by email about his book Inmates, I suggested that he had some interesting ideas, and asked if he thought they would be generally adopted. He was kind enough to answer, stating something to the effect that he wanted to make money.

I understood this to mean that perhaps his software would be more people-friendly and would represent its own compelling selling proposition. Contrarily, I think that the WinTel model is ubiquitous and a tremendous upheaval would occur if software were to radically change.

For the example in this section, we will take the middle road. We will assume that the user doesn't want to be prompted to verify any action but the option is a user-configurable option. (Perhaps in tutorial mode, the verification prompts would be presented.)

CAUTION

A production system must replace the verification screens with an undo capability. For example, deleting a record from a database needs to be undoable, especially if the user isn't prompted simply because this represents a significant departure from many implementations.

Although Cooper condemns abdicating responsibility to the user, writing software that is smarter—for example, can undo a delete record—is significantly more challenging than writing software that displays a verification dialog box.

The metaphor used to implement the configurable behavior is represented by a menu option and a checkbox. In a production system, you might represent this behavior with an Options dialog box and persist the choice to a user options table of the Registry. (Keep in mind that the purpose of this example is to demonstrate dynamic typecasting.) To try the example, open DynamicTypeCast.sln from this book's companion web site or create a Windows application and add a MainMenu with a Tools, Prompt On Close menu item and a checkbox. Complete the following steps to re-create the example:

  1. Create a Windows application.

  2. Add a MainMenu with a Tools, Prompt On Close menu item.

  3. Add a Checkbox control to the form.

  4. Double-click on the Prompt On Close menu item to generate the Click event handler (shown in Listing 5).

  5. Add CheckBox1.CheckedChanged to the Handles clause of the event handler.

Complete the numbered steps and add the code as shown in Listing 5. A synopsis of the code follows the listing.

Listing 5—A Single Event Handler Maintaining the State of a User-Configurable Option

 1: Private FClosePrompt As Boolean = False
 2:
 3: Property ClosePrompt() As Boolean
 4:  Get
 5:   Return FClosePrompt
 6:  End Get
 7:  Set(ByVal Value As Boolean)
 8:   FClosePrompt = Value
 9:   Changed(Value)
10:  End Set
11: End Property
12:
13: Private Sub Changed(ByVal Checked As Boolean)
14:  MenuItem4.Checked = Checked
15:  CheckBox1.Checked = Checked
16: End Sub
17:
18: Private Sub MenuItem4_Click(ByVal sender As System.Object, _
19:  ByVal e As System.EventArgs) _
20:  Handles MenuItem4.Click, CheckBox1.CheckedChanged
21:  Static semaphore As Boolean = False
22:  If (semaphore) Then Exit Sub
23:  semaphore = True
24:
25:  Try
26:
27:   If (TypeOf sender Is MenuItem) Then
28:    ClosePrompt = Not CType(sender, MenuItem).Checked
29:   Else
30:    ClosePrompt = CType(sender, CheckBox).Checked
31:   End If
32:
33:  Finally
34:   semaphore = False
35:  End Try
36:
37: End Sub

Note that the Handles clause on line 19 indicates that MenuItem4_Click handles MenuItem4.Click and CheckBox1.CheckedChanged events. Because both events have the same Delegate type, the single handler can handle both types of events. The Private field FClosePrompt on line 1 maintains the state, and by default is initialized to False. The property ClosePrompt is defined on lines 3 through 11. Line 8 stores the new ClosePrompt state and line 9 calls the Changed method, which synchronizes both controls' Checked states. Lines 18 through 37 define the single event handler.

Line 21 defines a variable named semaphore. This variable is Static to ensure that the event handler maintains the state of the semaphore between calls. It will be apparent soon why the semaphore is used. Line 22 exits if semaphore is True, and line 23 sets semaphore to True. Line 25 starts a Try Finally block with the Finally setting semaphore to False.

The If condition on lines 27 through 31 sets the value of ClosePrompt depending on whether sender is the MenuItem or CheckBox. If sender is a MenuItem, the Checked property is toggled with a Not statement; otherwise, the Checked property of the CheckBox already has the correct state. Setting ClosePrompt takes care of synchronizing the controls.

What does the semaphore do? The MenuItem part of the If statement updates ClosePrompt, which effectively changes the value of the CheckBox.Checked state. Changing this state causes the event handler to be called recursively, halfway through. The semaphore prevents the code from being executed a second time unnecessarily by making the event handler behave like an empty subroutine until the handler has completely finished the first time.

TIP

IntelliSense can provide member information to typecast objects at design time.

It's important to note that the CType function takes an Object and a class. The Object is cast to the type of the class. If the Object doesn't represent the class, a System.InvalidCastException occurs. For example, line 28 casts sender as a MenuItem; however, if sender is actually a Checkbox, this line would cause a System.InvalidCastException. After sender is cast, the members of the cast type can be used.

Using the EventHandler System.EventArgs Argument

The EventHandler is defined to pass a second argument, System.EventArgs. For generic events like Click, the EventArgs argument does not play a big role. However, it does act as a placeholder for more advanced events, like Paint. Paint uses a subclass of EventArgs to pass an instance of the device context wrapped in the Graphics class to the paint event handler.

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020