如何:实现基于事件的异步模式的客户端
下面的代码示例展示了如何使用符合基于事件的异步模式概述要求的组件。 此示例的窗体使用PrimeNumberCalculator
中介绍的 PrimeNumberCalculator
组件。
运行使用此示例的项目时,将会看到包含一个网格和两个按钮(“启动新任务”和“取消”)的“质数计算器”窗体。 可以连续多次单击“启动新任务”按钮。每次单击后,异步操作都会开始计算,以确定随机生成的测试数字是否为质数。 窗体会定期显示进度和增量结果。 每个操作都分配有唯一的任务 ID。 计算结果显示在“结果”列中;如果测试数字不是质数,将会把它标记为“复合”,并显示第一个除数。
可使用“取消”按钮,取消任意挂起的操作。 可以多选。
注意
数字大多不是质数。 如果在多个操作已完成后找不到质数,只需启动更多任务,最终会找到一些质数。
示例
using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Globalization;
using System.Threading;
using System.Windows.Forms;
namespace AsyncPattern
{
// This form tests the PrimeNumberCalculator component.
public class PrimeNumberCalculatorMain : System.Windows.Forms.Form
{
/////////////////////////////////////////////////////////////
// Private fields
//
#region Private fields
private PrimeNumberCalculator primeNumberCalculator1;
private System.Windows.Forms.GroupBox taskGroupBox;
private System.Windows.Forms.ListView listView1;
private System.Windows.Forms.ColumnHeader taskIdColHeader;
private System.Windows.Forms.ColumnHeader progressColHeader;
private System.Windows.Forms.ColumnHeader currentColHeader;
private System.Windows.Forms.Panel buttonPanel;
private System.Windows.Forms.Panel panel2;
private System.Windows.Forms.Button startAsyncButton;
private System.Windows.Forms.Button cancelButton;
private System.Windows.Forms.ColumnHeader testNumberColHeader;
private System.Windows.Forms.ColumnHeader resultColHeader;
private System.Windows.Forms.ColumnHeader firstDivisorColHeader;
private System.ComponentModel.IContainer components;
private int progressCounter;
private int progressInterval = 100;
#endregion // Private fields
/////////////////////////////////////////////////////////////
// Construction and destruction
//
#region Private fields
public PrimeNumberCalculatorMain ()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
// Hook up event handlers.
this.primeNumberCalculator1.CalculatePrimeCompleted +=
new CalculatePrimeCompletedEventHandler(
primeNumberCalculator1_CalculatePrimeCompleted);
this.primeNumberCalculator1.ProgressChanged +=
new ProgressChangedEventHandler(
primeNumberCalculator1_ProgressChanged);
this.listView1.SelectedIndexChanged +=
new EventHandler(listView1_SelectedIndexChanged);
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#endregion // Construction and destruction
/////////////////////////////////////////////////////////////
//
#region Implementation
// This event handler selects a number randomly to test
// for primality. It then starts the asynchronous
// calculation by calling the PrimeNumberCalculator
// component's CalculatePrimeAsync method.
private void startAsyncButton_Click (
System.Object sender, System.EventArgs e)
{
// Randomly choose test numbers
// up to 200,000 for primality.
Random rand = new Random();
int testNumber = rand.Next(200000);
// Task IDs are Guids.
Guid taskId = Guid.NewGuid();
this.AddListViewItem(taskId, testNumber);
// Start the asynchronous task.
this.primeNumberCalculator1.CalculatePrimeAsync(
testNumber,
taskId);
}
private void listView1_SelectedIndexChanged(
object sender,
EventArgs e)
{
this.cancelButton.Enabled = CanCancel();
}
// This event handler cancels all pending tasks that are
// selected in the ListView control.
private void cancelButton_Click(
System.Object sender,
System.EventArgs e)
{
Guid taskId = Guid.Empty;
// Cancel all selected tasks.
foreach(ListViewItem lvi in this.listView1.SelectedItems)
{
// Tasks that have been completed or canceled have
// their corresponding ListViewItem.Tag property
// set to null.
if (lvi.Tag != null)
{
taskId = (Guid)lvi.Tag;
this.primeNumberCalculator1.CancelAsync(taskId);
lvi.Selected = false;
}
}
cancelButton.Enabled = false;
}
// This event handler updates the ListView control when the
// PrimeNumberCalculator raises the ProgressChanged event.
//
// On fast computers, the PrimeNumberCalculator can raise many
// successive ProgressChanged events, so the user interface
// may be flooded with messages. To prevent the user interface
// from hanging, progress is only reported at intervals.
private void primeNumberCalculator1_ProgressChanged(
ProgressChangedEventArgs e)
{
if (this.progressCounter++ % this.progressInterval == 0)
{
Guid taskId = (Guid)e.UserState;
if (e is CalculatePrimeProgressChangedEventArgs)
{
CalculatePrimeProgressChangedEventArgs cppcea =
e as CalculatePrimeProgressChangedEventArgs;
this.UpdateListViewItem(
taskId,
cppcea.ProgressPercentage,
cppcea.LatestPrimeNumber);
}
else
{
this.UpdateListViewItem(
taskId,
e.ProgressPercentage);
}
}
else if (this.progressCounter > this.progressInterval)
{
this.progressCounter = 0;
}
}
// This event handler updates the ListView control when the
// PrimeNumberCalculator raises the CalculatePrimeCompleted
// event. The ListView item is updated with the appropriate
// outcome of the calculation: Canceled, Error, or result.
private void primeNumberCalculator1_CalculatePrimeCompleted(
object sender,
CalculatePrimeCompletedEventArgs e)
{
Guid taskId = (Guid)e.UserState;
if (e.Cancelled)
{
string result = "Canceled";
ListViewItem lvi = UpdateListViewItem(taskId, result);
if (lvi != null)
{
lvi.BackColor = Color.Pink;
lvi.Tag = null;
}
}
else if (e.Error != null)
{
string result = "Error";
ListViewItem lvi = UpdateListViewItem(taskId, result);
if (lvi != null)
{
lvi.BackColor = Color.Red;
lvi.ForeColor = Color.White;
lvi.Tag = null;
}
}
else
{
bool result = e.IsPrime;
ListViewItem lvi = UpdateListViewItem(
taskId,
result,
e.FirstDivisor);
if (lvi != null)
{
lvi.BackColor = Color.LightGray;
lvi.Tag = null;
}
}
}
#endregion // Implementation
/////////////////////////////////////////////////////////////
//
#region Private Methods
private ListViewItem AddListViewItem(
Guid guid,
int testNumber )
{
ListViewItem lvi = new ListViewItem();
lvi.Text = testNumber.ToString(
CultureInfo.CurrentCulture.NumberFormat);
lvi.SubItems.Add("Not Started");
lvi.SubItems.Add("1");
lvi.SubItems.Add(guid.ToString());
lvi.SubItems.Add("---");
lvi.SubItems.Add("---");
lvi.Tag = guid;
this.listView1.Items.Add( lvi );
return lvi;
}
private ListViewItem UpdateListViewItem(
Guid guid,
int percentComplete,
int current )
{
ListViewItem lviRet = null;
foreach (ListViewItem lvi in this.listView1.Items)
{
if (lvi.Tag != null)
{
if ((Guid)lvi.Tag == guid)
{
lvi.SubItems[1].Text =
percentComplete.ToString(
CultureInfo.CurrentCulture.NumberFormat);
lvi.SubItems[2].Text =
current.ToString(
CultureInfo.CurrentCulture.NumberFormat);
lviRet = lvi;
break;
}
}
}
return lviRet;
}
private ListViewItem UpdateListViewItem(
Guid guid,
int percentComplete,
int current,
bool result,
int firstDivisor )
{
ListViewItem lviRet = null;
foreach (ListViewItem lvi in this.listView1.Items)
{
if ((Guid)lvi.Tag == guid)
{
lvi.SubItems[1].Text =
percentComplete.ToString(
CultureInfo.CurrentCulture.NumberFormat);
lvi.SubItems[2].Text =
current.ToString(
CultureInfo.CurrentCulture.NumberFormat);
lvi.SubItems[4].Text =
result ? "Prime" : "Composite";
lvi.SubItems[5].Text =
firstDivisor.ToString(
CultureInfo.CurrentCulture.NumberFormat);
lviRet = lvi;
break;
}
}
return lviRet;
}
private ListViewItem UpdateListViewItem(
Guid guid,
int percentComplete )
{
ListViewItem lviRet = null;
foreach (ListViewItem lvi in this.listView1.Items)
{
if (lvi.Tag != null)
{
if ((Guid)lvi.Tag == guid)
{
lvi.SubItems[1].Text =
percentComplete.ToString(
CultureInfo.CurrentCulture.NumberFormat);
lviRet = lvi;
break;
}
}
}
return lviRet;
}
private ListViewItem UpdateListViewItem(
Guid guid,
bool result,
int firstDivisor )
{
ListViewItem lviRet = null;
foreach (ListViewItem lvi in this.listView1.Items)
{
if (lvi.Tag != null)
{
if ((Guid)lvi.Tag == guid)
{
lvi.SubItems[4].Text =
result ? "Prime" : "Composite";
lvi.SubItems[5].Text =
firstDivisor.ToString(
CultureInfo.CurrentCulture.NumberFormat);
lviRet = lvi;
break;
}
}
}
return lviRet;
}
private ListViewItem UpdateListViewItem(
Guid guid,
string result)
{
ListViewItem lviRet = null;
foreach (ListViewItem lvi in this.listView1.Items)
{
if (lvi.Tag != null)
{
if ((Guid)lvi.Tag == guid)
{
lvi.SubItems[4].Text = result;
lviRet = lvi;
break;
}
}
}
return lviRet;
}
private bool CanCancel()
{
bool oneIsActive = false;
foreach(ListViewItem lvi in this.listView1.SelectedItems)
{
if (lvi.Tag != null)
{
oneIsActive = true;
break;
}
}
return( oneIsActive == true );
}
#endregion
#region Windows Form Designer generated code
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.taskGroupBox = new System.Windows.Forms.GroupBox();
this.buttonPanel = new System.Windows.Forms.Panel();
this.cancelButton = new System.Windows.Forms.Button();
this.startAsyncButton = new System.Windows.Forms.Button();
this.listView1 = new System.Windows.Forms.ListView();
this.testNumberColHeader = new System.Windows.Forms.ColumnHeader();
this.progressColHeader = new System.Windows.Forms.ColumnHeader();
this.currentColHeader = new System.Windows.Forms.ColumnHeader();
this.taskIdColHeader = new System.Windows.Forms.ColumnHeader();
this.resultColHeader = new System.Windows.Forms.ColumnHeader();
this.firstDivisorColHeader = new System.Windows.Forms.ColumnHeader();
this.panel2 = new System.Windows.Forms.Panel();
this.primeNumberCalculator1 = new AsyncPattern.PrimeNumberCalculator(this.components);
this.taskGroupBox.SuspendLayout();
this.buttonPanel.SuspendLayout();
this.SuspendLayout();
//
// taskGroupBox
//
this.taskGroupBox.Controls.Add(this.buttonPanel);
this.taskGroupBox.Controls.Add(this.listView1);
this.taskGroupBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.taskGroupBox.Location = new System.Drawing.Point(0, 0);
this.taskGroupBox.Name = "taskGroupBox";
this.taskGroupBox.Size = new System.Drawing.Size(608, 254);
this.taskGroupBox.TabIndex = 1;
this.taskGroupBox.TabStop = false;
this.taskGroupBox.Text = "Tasks";
//
// buttonPanel
//
this.buttonPanel.Controls.Add(this.cancelButton);
this.buttonPanel.Controls.Add(this.startAsyncButton);
this.buttonPanel.Dock = System.Windows.Forms.DockStyle.Bottom;
this.buttonPanel.Location = new System.Drawing.Point(3, 176);
this.buttonPanel.Name = "buttonPanel";
this.buttonPanel.Size = new System.Drawing.Size(602, 75);
this.buttonPanel.TabIndex = 1;
//
// cancelButton
//
this.cancelButton.Enabled = false;
this.cancelButton.Location = new System.Drawing.Point(128, 24);
this.cancelButton.Name = "cancelButton";
this.cancelButton.Size = new System.Drawing.Size(88, 23);
this.cancelButton.TabIndex = 1;
this.cancelButton.Text = "Cancel";
this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click);
//
// startAsyncButton
//
this.startAsyncButton.Location = new System.Drawing.Point(24, 24);
this.startAsyncButton.Name = "startAsyncButton";
this.startAsyncButton.Size = new System.Drawing.Size(88, 23);
this.startAsyncButton.TabIndex = 0;
this.startAsyncButton.Text = "Start New Task";
this.startAsyncButton.Click += new System.EventHandler(this.startAsyncButton_Click);
//
// listView1
//
this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.testNumberColHeader,
this.progressColHeader,
this.currentColHeader,
this.taskIdColHeader,
this.resultColHeader,
this.firstDivisorColHeader});
this.listView1.Dock = System.Windows.Forms.DockStyle.Fill;
this.listView1.FullRowSelect = true;
this.listView1.GridLines = true;
this.listView1.Location = new System.Drawing.Point(3, 16);
this.listView1.Name = "listView1";
this.listView1.Size = new System.Drawing.Size(602, 160);
this.listView1.TabIndex = 0;
this.listView1.View = System.Windows.Forms.View.Details;
//
// testNumberColHeader
//
this.testNumberColHeader.Text = "Test Number";
this.testNumberColHeader.Width = 80;
//
// progressColHeader
//
this.progressColHeader.Text = "Progress";
//
// currentColHeader
//
this.currentColHeader.Text = "Current";
//
// taskIdColHeader
//
this.taskIdColHeader.Text = "Task ID";
this.taskIdColHeader.Width = 200;
//
// resultColHeader
//
this.resultColHeader.Text = "Result";
this.resultColHeader.Width = 80;
//
// firstDivisorColHeader
//
this.firstDivisorColHeader.Text = "First Divisor";
this.firstDivisorColHeader.Width = 80;
//
// panel2
//
this.panel2.Location = new System.Drawing.Point(200, 128);
this.panel2.Name = "panel2";
this.panel2.TabIndex = 2;
//
// PrimeNumberCalculatorMain
//
this.ClientSize = new System.Drawing.Size(608, 254);
this.Controls.Add(this.taskGroupBox);
this.Name = "PrimeNumberCalculatorMain";
this.Text = "Prime Number Calculator";
this.taskGroupBox.ResumeLayout(false);
this.buttonPanel.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
}
/////////////////////////////////////////////////////////////
#region PrimeNumberCalculator Implementation
public delegate void ProgressChangedEventHandler(
ProgressChangedEventArgs e);
public delegate void CalculatePrimeCompletedEventHandler(
object sender,
CalculatePrimeCompletedEventArgs e);
// This class implements the Event-based Asynchronous Pattern.
// It asynchronously computes whether a number is prime or
// composite (not prime).
public class PrimeNumberCalculator : Component
{
private delegate void WorkerEventHandler(
int numberToCheck,
AsyncOperation asyncOp);
private SendOrPostCallback onProgressReportDelegate;
private SendOrPostCallback onCompletedDelegate;
private HybridDictionary userStateToLifetime =
new HybridDictionary();
private System.ComponentModel.Container components = null;
/////////////////////////////////////////////////////////////
#region Public events
public event ProgressChangedEventHandler ProgressChanged;
public event CalculatePrimeCompletedEventHandler CalculatePrimeCompleted;
#endregion
/////////////////////////////////////////////////////////////
#region Construction and destruction
public PrimeNumberCalculator(IContainer container)
{
container.Add(this);
InitializeComponent();
InitializeDelegates();
}
public PrimeNumberCalculator()
{
InitializeComponent();
InitializeDelegates();
}
protected virtual void InitializeDelegates()
{
onProgressReportDelegate =
new SendOrPostCallback(ReportProgress);
onCompletedDelegate =
new SendOrPostCallback(CalculateCompleted);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
#endregion // Construction and destruction
/////////////////////////////////////////////////////////////
///
#region Implementation
// This method starts an asynchronous calculation.
// First, it checks the supplied task ID for uniqueness.
// If taskId is unique, it creates a new WorkerEventHandler
// and calls its BeginInvoke method to start the calculation.
public virtual void CalculatePrimeAsync(
int numberToTest,
object taskId)
{
// Create an AsyncOperation for taskId.
AsyncOperation asyncOp =
AsyncOperationManager.CreateOperation(taskId);
// Multiple threads will access the task dictionary,
// so it must be locked to serialize access.
lock (userStateToLifetime.SyncRoot)
{
if (userStateToLifetime.Contains(taskId))
{
throw new ArgumentException(
"Task ID parameter must be unique",
"taskId");
}
userStateToLifetime[taskId] = asyncOp;
}
// Start the asynchronous operation.
WorkerEventHandler workerDelegate = new WorkerEventHandler(CalculateWorker);
workerDelegate.BeginInvoke(
numberToTest,
asyncOp,
null,
null);
}
// Utility method for determining if a
// task has been canceled.
private bool TaskCanceled(object taskId)
{
return( userStateToLifetime[taskId] == null );
}
// This method cancels a pending asynchronous operation.
public void CancelAsync(object taskId)
{
AsyncOperation asyncOp = userStateToLifetime[taskId] as AsyncOperation;
if (asyncOp != null)
{
lock (userStateToLifetime.SyncRoot)
{
userStateToLifetime.Remove(taskId);
}
}
}
// This method performs the actual prime number computation.
// It is executed on the worker thread.
private void CalculateWorker(
int numberToTest,
AsyncOperation asyncOp)
{
bool isPrime = false;
int firstDivisor = 1;
Exception e = null;
// Check that the task is still active.
// The operation may have been canceled before
// the thread was scheduled.
if (!TaskCanceled(asyncOp.UserSuppliedState))
{
try
{
// Find all the prime numbers up to
// the square root of numberToTest.
ArrayList primes = BuildPrimeNumberList(
numberToTest,
asyncOp);
// Now we have a list of primes less than
// numberToTest.
isPrime = IsPrime(
primes,
numberToTest,
out firstDivisor);
}
catch (Exception ex)
{
e = ex;
}
}
//CalculatePrimeState calcState = new CalculatePrimeState(
// numberToTest,
// firstDivisor,
// isPrime,
// e,
// TaskCanceled(asyncOp.UserSuppliedState),
// asyncOp);
//this.CompletionMethod(calcState);
this.CompletionMethod(
numberToTest,
firstDivisor,
isPrime,
e,
TaskCanceled(asyncOp.UserSuppliedState),
asyncOp);
//completionMethodDelegate(calcState);
}
// This method computes the list of prime numbers used by the
// IsPrime method.
private ArrayList BuildPrimeNumberList(
int numberToTest,
AsyncOperation asyncOp)
{
ProgressChangedEventArgs e = null;
ArrayList primes = new ArrayList();
int firstDivisor;
int n = 5;
// Add the first prime numbers.
primes.Add(2);
primes.Add(3);
// Do the work.
while (n < numberToTest &&
!TaskCanceled( asyncOp.UserSuppliedState ) )
{
if (IsPrime(primes, n, out firstDivisor))
{
// Report to the client that a prime was found.
e = new CalculatePrimeProgressChangedEventArgs(
n,
(int)((float)n / (float)numberToTest * 100),
asyncOp.UserSuppliedState);
asyncOp.Post(this.onProgressReportDelegate, e);
primes.Add(n);
// Yield the rest of this time slice.
Thread.Sleep(0);
}
// Skip even numbers.
n += 2;
}
return primes;
}
// This method tests n for primality against the list of
// prime numbers contained in the primes parameter.
private bool IsPrime(
ArrayList primes,
int n,
out int firstDivisor)
{
bool foundDivisor = false;
bool exceedsSquareRoot = false;
int i = 0;
int divisor = 0;
firstDivisor = 1;
// Stop the search if:
// there are no more primes in the list,
// there is a divisor of n in the list, or
// there is a prime that is larger than
// the square root of n.
while (
(i < primes.Count) &&
!foundDivisor &&
!exceedsSquareRoot)
{
// The divisor variable will be the smallest
// prime number not yet tried.
divisor = (int)primes[i++];
// Determine whether the divisor is greater
// than the square root of n.
if (divisor * divisor > n)
{
exceedsSquareRoot = true;
}
// Determine whether the divisor is a factor of n.
else if (n % divisor == 0)
{
firstDivisor = divisor;
foundDivisor = true;
}
}
return !foundDivisor;
}
// This method is invoked via the AsyncOperation object,
// so it is guaranteed to be executed on the correct thread.
private void CalculateCompleted(object operationState)
{
CalculatePrimeCompletedEventArgs e =
operationState as CalculatePrimeCompletedEventArgs;
OnCalculatePrimeCompleted(e);
}
// This method is invoked via the AsyncOperation object,
// so it is guaranteed to be executed on the correct thread.
private void ReportProgress(object state)
{
ProgressChangedEventArgs e =
state as ProgressChangedEventArgs;
OnProgressChanged(e);
}
protected void OnCalculatePrimeCompleted(
CalculatePrimeCompletedEventArgs e)
{
if (CalculatePrimeCompleted != null)
{
CalculatePrimeCompleted(this, e);
}
}
protected void OnProgressChanged(ProgressChangedEventArgs e)
{
if (ProgressChanged != null)
{
ProgressChanged(e);
}
}
// This is the method that the underlying, free-threaded
// asynchronous behavior will invoke. This will happen on
// an arbitrary thread.
private void CompletionMethod(
int numberToTest,
int firstDivisor,
bool isPrime,
Exception exception,
bool canceled,
AsyncOperation asyncOp )
{
// If the task was not previously canceled,
// remove the task from the lifetime collection.
if (!canceled)
{
lock (userStateToLifetime.SyncRoot)
{
userStateToLifetime.Remove(asyncOp.UserSuppliedState);
}
}
// Package the results of the operation in a
// CalculatePrimeCompletedEventArgs.
CalculatePrimeCompletedEventArgs e =
new CalculatePrimeCompletedEventArgs(
numberToTest,
firstDivisor,
isPrime,
exception,
canceled,
asyncOp.UserSuppliedState);
// End the task. The asyncOp object is responsible
// for marshaling the call.
asyncOp.PostOperationCompleted(onCompletedDelegate, e);
// Note that after the call to OperationCompleted,
// asyncOp is no longer usable, and any attempt to use it
// will cause an exception to be thrown.
}
#endregion
/////////////////////////////////////////////////////////////
#region Component Designer generated code
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
}
public class CalculatePrimeProgressChangedEventArgs :
ProgressChangedEventArgs
{
private int latestPrimeNumberValue = 1;
public CalculatePrimeProgressChangedEventArgs(
int latestPrime,
int progressPercentage,
object userToken) : base( progressPercentage, userToken )
{
this.latestPrimeNumberValue = latestPrime;
}
public int LatestPrimeNumber
{
get
{
return latestPrimeNumberValue;
}
}
}
public class CalculatePrimeCompletedEventArgs :
AsyncCompletedEventArgs
{
private int numberToTestValue = 0;
private int firstDivisorValue = 1;
private bool isPrimeValue;
public CalculatePrimeCompletedEventArgs(
int numberToTest,
int firstDivisor,
bool isPrime,
Exception e,
bool canceled,
object state) : base(e, canceled, state)
{
this.numberToTestValue = numberToTest;
this.firstDivisorValue = firstDivisor;
this.isPrimeValue = isPrime;
}
public int NumberToTest
{
get
{
// Raise an exception if the operation failed or
// was canceled.
RaiseExceptionIfNecessary();
// If the operation was successful, return the
// property value.
return numberToTestValue;
}
}
public int FirstDivisor
{
get
{
// Raise an exception if the operation failed or
// was canceled.
RaiseExceptionIfNecessary();
// If the operation was successful, return the
// property value.
return firstDivisorValue;
}
}
public bool IsPrime
{
get
{
// Raise an exception if the operation failed or
// was canceled.
RaiseExceptionIfNecessary();
// If the operation was successful, return the
// property value.
return isPrimeValue;
}
}
}
#endregion
}
Imports System.Collections
Imports System.Collections.Specialized
Imports System.ComponentModel
Imports System.Drawing
Imports System.Globalization
Imports System.Threading
Imports System.Windows.Forms
' This form tests the PrimeNumberCalculator component.
Public Class PrimeNumberCalculatorMain
Inherits System.Windows.Forms.Form
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Private fields
'
#Region "Private fields"
Private WithEvents primeNumberCalculator1 As PrimeNumberCalculator
Private taskGroupBox As System.Windows.Forms.GroupBox
Private WithEvents listView1 As System.Windows.Forms.ListView
Private taskIdColHeader As System.Windows.Forms.ColumnHeader
Private progressColHeader As System.Windows.Forms.ColumnHeader
Private currentColHeader As System.Windows.Forms.ColumnHeader
Private buttonPanel As System.Windows.Forms.Panel
Private panel2 As System.Windows.Forms.Panel
Private WithEvents startAsyncButton As System.Windows.Forms.Button
Private WithEvents cancelAsyncButton As System.Windows.Forms.Button
Private testNumberColHeader As System.Windows.Forms.ColumnHeader
Private resultColHeader As System.Windows.Forms.ColumnHeader
Private firstDivisorColHeader As System.Windows.Forms.ColumnHeader
Private components As System.ComponentModel.IContainer
Private progressCounter As Integer
Private progressInterval As Integer = 100
#End Region
Public Sub New()
InitializeComponent()
End Sub
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If (components IsNot Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
#Region "Implementation"
' This event handler selects a number randomly to test
' for primality. It then starts the asynchronous
' calculation by calling the PrimeNumberCalculator
' component's CalculatePrimeAsync method.
Private Sub startAsyncButton_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles startAsyncButton.Click
' Randomly choose test numbers
' up to 200,000 for primality.
Dim rand As New Random
Dim testNumber As Integer = rand.Next(200000)
' Task IDs are Guids.
Dim taskId As Guid = Guid.NewGuid()
Me.AddListViewItem(taskId, testNumber)
' Start the asynchronous task.
Me.primeNumberCalculator1.CalculatePrimeAsync( _
testNumber, _
taskId)
End Sub
Private Sub listView1_SelectedIndexChanged( _
ByVal sender As Object, ByVal e As EventArgs) _
Handles listView1.SelectedIndexChanged
Me.cancelAsyncButton.Enabled = CanCancel()
End Sub
' This event handler cancels all pending tasks that are
' selected in the ListView control.
Private Sub cancelAsyncButton_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles cancelAsyncButton.Click
Dim taskId As Guid = Guid.Empty
' Cancel all selected tasks.
Dim lvi As ListViewItem
For Each lvi In Me.listView1.SelectedItems
' Tasks that have been completed or canceled have
' their corresponding ListViewItem.Tag property
' set to Nothing.
If (lvi.Tag IsNot Nothing) Then
taskId = CType(lvi.Tag, Guid)
Me.primeNumberCalculator1.CancelAsync(taskId)
lvi.Selected = False
End If
Next lvi
cancelAsyncButton.Enabled = False
End Sub
' This event handler updates the ListView control when the
' PrimeNumberCalculator raises the ProgressChanged event.
'
' On fast computers, the PrimeNumberCalculator can raise many
' successive ProgressChanged events, so the user interface
' may be flooded with messages. To prevent the user interface
' from hanging, progress is only reported at intervals.
Private Sub primeNumberCalculator1_ProgressChanged( _
ByVal e As ProgressChangedEventArgs) _
Handles primeNumberCalculator1.ProgressChanged
Me.progressCounter += 1
If Me.progressCounter Mod Me.progressInterval = 0 Then
Dim taskId As Guid = CType(e.UserState, Guid)
If TypeOf e Is CalculatePrimeProgressChangedEventArgs Then
Dim cppcea As CalculatePrimeProgressChangedEventArgs = e
Me.UpdateListViewItem( _
taskId, _
cppcea.ProgressPercentage, _
cppcea.LatestPrimeNumber)
Else
Me.UpdateListViewItem( _
taskId, e.ProgressPercentage)
End If
ElseIf Me.progressCounter > Me.progressInterval Then
Me.progressCounter = 0
End If
End Sub
' This event handler updates the ListView control when the
' PrimeNumberCalculator raises the CalculatePrimeCompleted
' event. The ListView item is updated with the appropriate
' outcome of the calculation: Canceled, Error, or result.
Private Sub primeNumberCalculator1_CalculatePrimeCompleted( _
ByVal sender As Object, _
ByVal e As CalculatePrimeCompletedEventArgs) _
Handles primeNumberCalculator1.CalculatePrimeCompleted
Dim taskId As Guid = CType(e.UserState, Guid)
If e.Cancelled Then
Dim result As String = "Canceled"
Dim lvi As ListViewItem = UpdateListViewItem( _
taskId, _
result)
If (lvi IsNot Nothing) Then
lvi.BackColor = Color.Pink
lvi.Tag = Nothing
End If
ElseIf e.Error IsNot Nothing Then
Dim result As String = "Error"
Dim lvi As ListViewItem = UpdateListViewItem( _
taskId, result)
If (lvi IsNot Nothing) Then
lvi.BackColor = Color.Red
lvi.ForeColor = Color.White
lvi.Tag = Nothing
End If
Else
Dim result As Boolean = e.IsPrime
Dim lvi As ListViewItem = UpdateListViewItem( _
taskId, _
result, _
e.FirstDivisor)
If (lvi IsNot Nothing) Then
lvi.BackColor = Color.LightGray
lvi.Tag = Nothing
End If
End If
End Sub
#End Region
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
#Region "Private Methods"
Private Function AddListViewItem( _
ByVal guid As Guid, _
ByVal testNumber As Integer) As ListViewItem
Dim lvi As New ListViewItem
lvi.Text = testNumber.ToString( _
CultureInfo.CurrentCulture.NumberFormat)
lvi.SubItems.Add("Not Started")
lvi.SubItems.Add("1")
lvi.SubItems.Add(guid.ToString())
lvi.SubItems.Add("---")
lvi.SubItems.Add("---")
lvi.Tag = guid
Me.listView1.Items.Add(lvi)
Return lvi
End Function
Private Overloads Function UpdateListViewItem( _
ByVal guid As Guid, _
ByVal percentComplete As Integer, _
ByVal current As Integer) As ListViewItem
Dim lviRet As ListViewItem = Nothing
Dim lvi As ListViewItem
For Each lvi In Me.listView1.Items
If (lvi.Tag IsNot Nothing) Then
If guid.CompareTo(CType(lvi.Tag, Guid)) = 0 Then
lvi.SubItems(1).Text = percentComplete.ToString( _
CultureInfo.CurrentCulture.NumberFormat)
lvi.SubItems(2).Text = current.ToString( _
CultureInfo.CurrentCulture.NumberFormat)
lviRet = lvi
Exit For
End If
End If
Next lvi
Return lviRet
End Function
Private Overloads Function UpdateListViewItem( _
ByVal guid As Guid, _
ByVal percentComplete As Integer, _
ByVal current As Integer, _
ByVal result As Boolean, _
ByVal firstDivisor As Integer) As ListViewItem
Dim lviRet As ListViewItem = Nothing
Dim lvi As ListViewItem
For Each lvi In Me.listView1.Items
If guid.CompareTo(CType(lvi.Tag, Guid)) = 0 Then
lvi.SubItems(1).Text = percentComplete.ToString( _
CultureInfo.CurrentCulture.NumberFormat)
lvi.SubItems(2).Text = current.ToString( _
CultureInfo.CurrentCulture.NumberFormat)
lvi.SubItems(4).Text = _
IIf(result, "Prime", "Composite")
lvi.SubItems(5).Text = firstDivisor.ToString( _
CultureInfo.CurrentCulture.NumberFormat)
lviRet = lvi
Exit For
End If
Next lvi
Return lviRet
End Function
Private Overloads Function UpdateListViewItem( _
ByVal guid As Guid, _
ByVal percentComplete As Integer) As ListViewItem
Dim lviRet As ListViewItem = Nothing
Dim lvi As ListViewItem
For Each lvi In Me.listView1.Items
If (lvi.Tag IsNot Nothing) Then
If guid.CompareTo(CType(lvi.Tag, Guid)) = 0 Then
lvi.SubItems(1).Text = percentComplete.ToString( _
CultureInfo.CurrentCulture.NumberFormat)
lviRet = lvi
Exit For
End If
End If
Next lvi
Return lviRet
End Function
Private Overloads Function UpdateListViewItem( _
ByVal guid As Guid, _
ByVal result As Boolean, _
ByVal firstDivisor As Integer) As ListViewItem
Dim lviRet As ListViewItem = Nothing
Dim lvi As ListViewItem
For Each lvi In Me.listView1.Items
If (lvi.Tag IsNot Nothing) Then
If guid.CompareTo(CType(lvi.Tag, Guid)) = 0 Then
lvi.SubItems(4).Text = _
IIf(result, "Prime", "Composite")
lvi.SubItems(5).Text = firstDivisor.ToString( _
CultureInfo.CurrentCulture.NumberFormat)
lviRet = lvi
Exit For
End If
End If
Next lvi
Return lviRet
End Function
Private Overloads Function UpdateListViewItem( _
ByVal guid As Guid, _
ByVal result As String) As ListViewItem
Dim lviRet As ListViewItem = Nothing
Dim lvi As ListViewItem
For Each lvi In Me.listView1.Items
If (lvi.Tag IsNot Nothing) Then
If guid.CompareTo(CType(lvi.Tag, Guid)) = 0 Then
lvi.SubItems(4).Text = result
lviRet = lvi
Exit For
End If
End If
Next lvi
Return lviRet
End Function
Private Function CanCancel() As Boolean
Dim oneIsActive As Boolean = False
Dim lvi As ListViewItem
For Each lvi In Me.listView1.SelectedItems
If (lvi.Tag IsNot Nothing) Then
oneIsActive = True
Exit For
End If
Next lvi
Return oneIsActive = True
End Function
#End Region
Private Sub InitializeComponent()
Me.components = New System.ComponentModel.Container
Me.taskGroupBox = New System.Windows.Forms.GroupBox
Me.buttonPanel = New System.Windows.Forms.Panel
Me.cancelAsyncButton = New System.Windows.Forms.Button
Me.startAsyncButton = New System.Windows.Forms.Button
Me.listView1 = New System.Windows.Forms.ListView
Me.testNumberColHeader = New System.Windows.Forms.ColumnHeader
Me.progressColHeader = New System.Windows.Forms.ColumnHeader
Me.currentColHeader = New System.Windows.Forms.ColumnHeader
Me.taskIdColHeader = New System.Windows.Forms.ColumnHeader
Me.resultColHeader = New System.Windows.Forms.ColumnHeader
Me.firstDivisorColHeader = New System.Windows.Forms.ColumnHeader
Me.panel2 = New System.Windows.Forms.Panel
Me.primeNumberCalculator1 = New PrimeNumberCalculator(Me.components)
Me.taskGroupBox.SuspendLayout()
Me.buttonPanel.SuspendLayout()
Me.SuspendLayout()
'
' taskGroupBox
'
Me.taskGroupBox.Controls.Add(Me.buttonPanel)
Me.taskGroupBox.Controls.Add(Me.listView1)
Me.taskGroupBox.Dock = System.Windows.Forms.DockStyle.Fill
Me.taskGroupBox.Location = New System.Drawing.Point(0, 0)
Me.taskGroupBox.Name = "taskGroupBox"
Me.taskGroupBox.Size = New System.Drawing.Size(608, 254)
Me.taskGroupBox.TabIndex = 1
Me.taskGroupBox.TabStop = False
Me.taskGroupBox.Text = "Tasks"
'
' buttonPanel
'
Me.buttonPanel.Controls.Add(Me.cancelAsyncButton)
Me.buttonPanel.Controls.Add(Me.startAsyncButton)
Me.buttonPanel.Dock = System.Windows.Forms.DockStyle.Bottom
Me.buttonPanel.Location = New System.Drawing.Point(3, 176)
Me.buttonPanel.Name = "buttonPanel"
Me.buttonPanel.Size = New System.Drawing.Size(602, 75)
Me.buttonPanel.TabIndex = 1
'
' cancelAsyncButton
'
Me.cancelAsyncButton.Enabled = False
Me.cancelAsyncButton.Location = New System.Drawing.Point(128, 24)
Me.cancelAsyncButton.Name = "cancelAsyncButton"
Me.cancelAsyncButton.Size = New System.Drawing.Size(88, 23)
Me.cancelAsyncButton.TabIndex = 1
Me.cancelAsyncButton.Text = "Cancel"
'
' startAsyncButton
'
Me.startAsyncButton.Location = New System.Drawing.Point(24, 24)
Me.startAsyncButton.Name = "startAsyncButton"
Me.startAsyncButton.Size = New System.Drawing.Size(88, 23)
Me.startAsyncButton.TabIndex = 0
Me.startAsyncButton.Text = "Start New Task"
'
' listView1
'
Me.listView1.Columns.AddRange(New System.Windows.Forms.ColumnHeader() {Me.testNumberColHeader, Me.progressColHeader, Me.currentColHeader, Me.taskIdColHeader, Me.resultColHeader, Me.firstDivisorColHeader})
Me.listView1.Dock = System.Windows.Forms.DockStyle.Fill
Me.listView1.FullRowSelect = True
Me.listView1.GridLines = True
Me.listView1.Location = New System.Drawing.Point(3, 16)
Me.listView1.Name = "listView1"
Me.listView1.Size = New System.Drawing.Size(602, 160)
Me.listView1.TabIndex = 0
Me.listView1.View = System.Windows.Forms.View.Details
'
' testNumberColHeader
'
Me.testNumberColHeader.Text = "Test Number"
Me.testNumberColHeader.Width = 80
'
' progressColHeader
'
Me.progressColHeader.Text = "Progress"
'
' currentColHeader
'
Me.currentColHeader.Text = "Current"
'
' taskIdColHeader
'
Me.taskIdColHeader.Text = "Task ID"
Me.taskIdColHeader.Width = 200
'
' resultColHeader
'
Me.resultColHeader.Text = "Result"
Me.resultColHeader.Width = 80
'
' firstDivisorColHeader
'
Me.firstDivisorColHeader.Text = "First Divisor"
Me.firstDivisorColHeader.Width = 80
'
' panel2
'
Me.panel2.Location = New System.Drawing.Point(200, 128)
Me.panel2.Name = "panel2"
Me.panel2.TabIndex = 2
'
' PrimeNumberCalculatorMain
'
Me.ClientSize = New System.Drawing.Size(608, 254)
Me.Controls.Add(taskGroupBox)
Me.Name = "PrimeNumberCalculatorMain"
Me.Text = "Prime Number Calculator"
Me.taskGroupBox.ResumeLayout(False)
Me.buttonPanel.ResumeLayout(False)
Me.ResumeLayout(False)
End Sub
End Class
Public Delegate Sub ProgressChangedEventHandler( _
ByVal e As ProgressChangedEventArgs)
Public Delegate Sub CalculatePrimeCompletedEventHandler( _
ByVal sender As Object, _
ByVal e As CalculatePrimeCompletedEventArgs)
' This class implements the Event-based Asynchronous Pattern.
' It asynchronously computes whether a number is prime or
' composite (not prime).
Public Class PrimeNumberCalculator
Inherits System.ComponentModel.Component
Private Delegate Sub WorkerEventHandler( _
ByVal numberToCheck As Integer, _
ByVal asyncOp As AsyncOperation)
Private onProgressReportDelegate As SendOrPostCallback
Private onCompletedDelegate As SendOrPostCallback
Private userStateToLifetime As New HybridDictionary()
Private components As System.ComponentModel.Container = Nothing
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
#Region "Public events"
Public Event ProgressChanged _
As ProgressChangedEventHandler
Public Event CalculatePrimeCompleted _
As CalculatePrimeCompletedEventHandler
#End Region
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
#Region "Construction and destruction"
Public Sub New(ByVal container As System.ComponentModel.IContainer)
container.Add(Me)
InitializeComponent()
InitializeDelegates()
End Sub
Public Sub New()
InitializeComponent()
InitializeDelegates()
End Sub
Protected Overridable Sub InitializeDelegates()
onProgressReportDelegate = _
New SendOrPostCallback(AddressOf ReportProgress)
onCompletedDelegate = _
New SendOrPostCallback(AddressOf CalculateCompleted)
End Sub
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If (components IsNot Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
#End Region
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
#Region "Implementation"
' This method starts an asynchronous calculation.
' First, it checks the supplied task ID for uniqueness.
' If taskId is unique, it creates a new WorkerEventHandler
' and calls its BeginInvoke method to start the calculation.
Public Overridable Sub CalculatePrimeAsync( _
ByVal numberToTest As Integer, _
ByVal taskId As Object)
' Create an AsyncOperation for taskId.
Dim asyncOp As AsyncOperation = _
AsyncOperationManager.CreateOperation(taskId)
' Multiple threads will access the task dictionary,
' so it must be locked to serialize access.
SyncLock userStateToLifetime.SyncRoot
If userStateToLifetime.Contains(taskId) Then
Throw New ArgumentException( _
"Task ID parameter must be unique", _
"taskId")
End If
userStateToLifetime(taskId) = asyncOp
End SyncLock
' Start the asynchronous operation.
Dim workerDelegate As New WorkerEventHandler( _
AddressOf CalculateWorker)
workerDelegate.BeginInvoke( _
numberToTest, _
asyncOp, _
Nothing, _
Nothing)
End Sub
' Utility method for determining if a
' task has been canceled.
Private Function TaskCanceled(ByVal taskId As Object) As Boolean
Return (userStateToLifetime(taskId) Is Nothing)
End Function
' This method cancels a pending asynchronous operation.
Public Sub CancelAsync(ByVal taskId As Object)
Dim obj As Object = userStateToLifetime(taskId)
If (obj IsNot Nothing) Then
SyncLock userStateToLifetime.SyncRoot
userStateToLifetime.Remove(taskId)
End SyncLock
End If
End Sub
' This method performs the actual prime number computation.
' It is executed on the worker thread.
Private Sub CalculateWorker( _
ByVal numberToTest As Integer, _
ByVal asyncOp As AsyncOperation)
Dim prime As Boolean = False
Dim firstDivisor As Integer = 1
Dim exc As Exception = Nothing
' Check that the task is still active.
' The operation may have been canceled before
' the thread was scheduled.
If Not Me.TaskCanceled(asyncOp.UserSuppliedState) Then
Try
' Find all the prime numbers up to the
' square root of numberToTest.
Dim primes As ArrayList = BuildPrimeNumberList( _
numberToTest, asyncOp)
' Now we have a list of primes less than
'numberToTest.
prime = IsPrime( _
primes, _
numberToTest, _
firstDivisor)
Catch ex As Exception
exc = ex
End Try
End If
Me.CompletionMethod( _
numberToTest, _
firstDivisor, _
prime, _
exc, _
TaskCanceled(asyncOp.UserSuppliedState), _
asyncOp)
End Sub
' This method computes the list of prime numbers used by the
' IsPrime method.
Private Function BuildPrimeNumberList( _
ByVal numberToTest As Integer, _
ByVal asyncOp As AsyncOperation) As ArrayList
Dim e As ProgressChangedEventArgs = Nothing
Dim primes As New ArrayList
Dim firstDivisor As Integer
Dim n As Integer = 5
' Add the first prime numbers.
primes.Add(2)
primes.Add(3)
' Do the work.
While n < numberToTest And _
Not Me.TaskCanceled(asyncOp.UserSuppliedState)
If IsPrime(primes, n, firstDivisor) Then
' Report to the client that you found a prime.
e = New CalculatePrimeProgressChangedEventArgs( _
n, _
CSng(n) / CSng(numberToTest) * 100, _
asyncOp.UserSuppliedState)
asyncOp.Post(Me.onProgressReportDelegate, e)
primes.Add(n)
' Yield the rest of this time slice.
Thread.Sleep(0)
End If
' Skip even numbers.
n += 2
End While
Return primes
End Function
' This method tests n for primality against the list of
' prime numbers contained in the primes parameter.
Private Function IsPrime( _
ByVal primes As ArrayList, _
ByVal n As Integer, _
ByRef firstDivisor As Integer) As Boolean
Dim foundDivisor As Boolean = False
Dim exceedsSquareRoot As Boolean = False
Dim i As Integer = 0
Dim divisor As Integer = 0
firstDivisor = 1
' Stop the search if:
' there are no more primes in the list,
' there is a divisor of n in the list, or
' there is a prime that is larger than
' the square root of n.
While i < primes.Count AndAlso _
Not foundDivisor AndAlso _
Not exceedsSquareRoot
' The divisor variable will be the smallest prime number
' not yet tried.
divisor = primes(i)
i = i + 1
' Determine whether the divisor is greater than the
' square root of n.
If divisor * divisor > n Then
exceedsSquareRoot = True
' Determine whether the divisor is a factor of n.
ElseIf n Mod divisor = 0 Then
firstDivisor = divisor
foundDivisor = True
End If
End While
Return Not foundDivisor
End Function
' This method is invoked via the AsyncOperation object,
' so it is guaranteed to be executed on the correct thread.
Private Sub CalculateCompleted(ByVal operationState As Object)
Dim e As CalculatePrimeCompletedEventArgs = operationState
OnCalculatePrimeCompleted(e)
End Sub
' This method is invoked via the AsyncOperation object,
' so it is guaranteed to be executed on the correct thread.
Private Sub ReportProgress(ByVal state As Object)
Dim e As ProgressChangedEventArgs = state
OnProgressChanged(e)
End Sub
Protected Sub OnCalculatePrimeCompleted( _
ByVal e As CalculatePrimeCompletedEventArgs)
RaiseEvent CalculatePrimeCompleted(Me, e)
End Sub
Protected Sub OnProgressChanged( _
ByVal e As ProgressChangedEventArgs)
RaiseEvent ProgressChanged(e)
End Sub
' This is the method that the underlying, free-threaded
' asynchronous behavior will invoke. This will happen on
' an arbitrary thread.
Private Sub CompletionMethod( _
ByVal numberToTest As Integer, _
ByVal firstDivisor As Integer, _
ByVal prime As Boolean, _
ByVal exc As Exception, _
ByVal canceled As Boolean, _
ByVal asyncOp As AsyncOperation)
' If the task was not previously canceled,
' remove the task from the lifetime collection.
If Not canceled Then
SyncLock userStateToLifetime.SyncRoot
userStateToLifetime.Remove(asyncOp.UserSuppliedState)
End SyncLock
End If
' Package the results of the operation in a
' CalculatePrimeCompletedEventArgs.
Dim e As New CalculatePrimeCompletedEventArgs( _
numberToTest, _
firstDivisor, _
prime, _
exc, _
canceled, _
asyncOp.UserSuppliedState)
' End the task. The asyncOp object is responsible
' for marshaling the call.
asyncOp.PostOperationCompleted(onCompletedDelegate, e)
' Note that after the call to PostOperationCompleted, asyncOp
' is no longer usable, and any attempt to use it will cause.
' an exception to be thrown.
End Sub
#End Region
Private Sub InitializeComponent()
End Sub
End Class
Public Class CalculatePrimeProgressChangedEventArgs
Inherits ProgressChangedEventArgs
Private latestPrimeNumberValue As Integer = 1
Public Sub New( _
ByVal latestPrime As Integer, _
ByVal progressPercentage As Integer, _
ByVal UserState As Object)
MyBase.New(progressPercentage, UserState)
Me.latestPrimeNumberValue = latestPrime
End Sub
Public ReadOnly Property LatestPrimeNumber() As Integer
Get
Return latestPrimeNumberValue
End Get
End Property
End Class
Public Class CalculatePrimeCompletedEventArgs
Inherits AsyncCompletedEventArgs
Private numberToTestValue As Integer = 0
Private firstDivisorValue As Integer = 1
Private isPrimeValue As Boolean
Public Sub New( _
ByVal numberToTest As Integer, _
ByVal firstDivisor As Integer, _
ByVal isPrime As Boolean, _
ByVal e As Exception, _
ByVal canceled As Boolean, _
ByVal state As Object)
MyBase.New(e, canceled, state)
Me.numberToTestValue = numberToTest
Me.firstDivisorValue = firstDivisor
Me.isPrimeValue = isPrime
End Sub
Public ReadOnly Property NumberToTest() As Integer
Get
' Raise an exception if the operation failed
' or was canceled.
RaiseExceptionIfNecessary()
' If the operation was successful, return
' the property value.
Return numberToTestValue
End Get
End Property
Public ReadOnly Property FirstDivisor() As Integer
Get
' Raise an exception if the operation failed
' or was canceled.
RaiseExceptionIfNecessary()
' If the operation was successful, return
' the property value.
Return firstDivisorValue
End Get
End Property
Public ReadOnly Property IsPrime() As Boolean
Get
' Raise an exception if the operation failed
' or was canceled.
RaiseExceptionIfNecessary()
' If the operation was successful, return
' the property value.
Return isPrimeValue
End Get
End Property
End Class