Manage program flow - Exam Ref 70-483: Programming in C# (2013)

Exam Ref 70-483: Programming in C# (2013)

Chapter 1. Manage program flow

If you could build only programs that execute all their logic from top to bottom, it would not be feasible to build complex applications. Fortunately, C# and the .NET Framework offer you a lot of options for creating complex programs that don’t have a fixed program flow.

This chapter starts with looking at how to create multithreaded applications. Those applications can scale well and remain responsive to the user while doing their work. You will also look at the new language feature async/await that was added to C# 5.

IMPORTANT

Have you read Preparing for the exam?

It contains valuable information regarding the skills you need to pass the exam.

You will learn about the basic C# language constructs to make decisions and execute a piece of code multiple times, depending on the circumstances. These constructs form the basic language blocks of each application, and you will use them often.

After that, you will learn how to create applications that are loosely coupled by using delegates and events. With events, you can build objects that can notify each other when something happens and that can respond to those notifications. Frameworks such as ASP.NET, Windows Presentation Foundation (WPF), and WinForms make heavy use of events; understanding events thoroughly will help you build great applications.

Unfortunately, your program flow can also be interrupted by errors. Such errors can happen in areas that are out of your control but that you need to respond to. Sometimes you want to raise such an error yourself. You will learn how exceptions can help you implement a robust error-handling strategy in your applications.

Objectives in this chapter:

§ Objective 1.1: Implement multithreading and asynchronous processing

§ Objective 1.2: Manage multithreading

§ Objective 1.3: Implement program flow

§ Objective 1.4: Create and implement events and callbacks

§ Objective 1.5: Implement exception handling

Objective 1.1: Implement multithreading and asynchronous processing

Applications are becoming more and more complex as user expectations rise. To fully take advantage of multicore systems and stay responsive, you need to create applications that use multiple threads, often called parallelism.

The .NET Framework and the C# language offer a lot of options that you can use to create multithreaded applications.

THIS OBJECTIVE COVERS HOW TO:

§ Understand threads.

§ Use the Task Parallel Library.

§ Use the Parallel class.

§ Use the new async and await keywords.

§ Use Parallel Language Integrated Query.

§ Use concurrent collections.

Understanding threads

Imagine that your computer has only one central processing unit (CPU) that is capable of executing only one operation at a time. Now, imagine what would happen if the CPU has to work hard to execute a task that takes a long time.

While this operation runs, all other operations would be paused. This means that the whole machine would freeze and appear unresponsive to the user. Things get even worse when that long-running operation contains a bug so it never ends. Because the rest of the machine is unusable, the only thing you can do is restart the machine.

To remedy this problem, the concept of a thread is used. In current versions of Windows, each application runs in its own process. A process isolates an application from other applications by giving it its own virtual memory and by ensuring that different processes can’t influence each other. Each process runs in its own thread. A thread is something like a virtualized CPU. If an application crashes or hits an infinite loop, only the application’s process is affected.

Windows must manage all of the threads to ensure they can do their work. These management tasks do come with an overhead. Each thread is allowed by Windows to execute for a certain time period. After this period ends, the thread is paused and Windows switches to another thread. This is called context switching.

In practice, this means that Windows has to do some work to make it happen. The current thread is using a certain area of memory; it uses CPU registers and other state data, and Windows has to make sure that the whole context of the thread is saved and restored on each switch.

But although there are certain performance hits, using threads does ensure that each process gets its time to execute without having to wait until all other operations finish. This improves the responsiveness of the system and gives the illusion that one CPU can execute multiple tasks at a time. This way you can create an application that uses parallelism, meaning that it can execute multiple threads on different CPUs in parallel.

Almost any device that you buy today has a CPU with multiple cores, which is similar to having multiple CPUs. Some servers not only have multicore CPUs but they also have more than one CPU. To make use of all these cores, you need multiple threads. Windows ensures that those threads are distributed over your available cores. This way you can perform multiple tasks at once and improve scalability.

Because of the associated overhead, you should carefully determine whether you need multithreading. But if you want to use threads for scalability or responsiveness, C# and .NET Framework offer you a lot of possibilities.

Using the Thread class

The Thread class can be found in the System.Threading namespace. This class enables you to create new treads, manage their priority, and get their status.

The Thread class isn’t something that you should use in your applications, except when you have special needs. However, when using the Thread class you have control over all configuration options. You can, for example, specify the priority of your thread, tell Windows that your thread is long running, or configure other advanced options.

Example 1-1 shows an example of using the Thread class to run a method on another thread. The Console class synchronizes the use of the output stream for you so you can write to it from multiple threads. Synchronization is the mechanism of ensuring that two threads don’t execute a specific portion of your program at the same time. In the case of a console application, this means that no two threads can write data to the screen at the exact same time. If one thread is working with the output stream, other threads will have to wait before it’s finished.

Example 1-1. Creating a thread with the Thread class

using System;

using System.Threading;

namespace Chapter1

{

public static class Program

{

public static void ThreadMethod()

{

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

{

Console.WriteLine("ThreadProc: {0}", i);

Thread.Sleep(0);

}

}

public static void Main()

{

Thread t = new Thread(new ThreadStart(ThreadMethod));

t.Start();

for (int i = 0; i < 4; i++)

{

Console.WriteLine("Main thread: Do some work.");

Thread.Sleep(0);

}

t.Join();

}

}

}

// Displays

//Main thread: Do some work.

//ThreadProc: 0

//Main thread: Do some work.

//ThreadProc: 1

//Main thread: Do some work.

//ThreadProc: 2

//Main thread: Do some work.

//ThreadProc: 3

//ThreadProc: 4

//ThreadProc: 5

//ThreadProc: 6

//ThreadProc: 7

//ThreadProc: 8

//ThreadProc: 9

//ThreadProc: 10

As you can see, both threads run and print their message to the console. The Thread.Join method is called on the main thread to let it wait until the other thread finishes.

Why the Thread.Sleep(0)? It is used to signal to Windows that this thread is finished. Instead of waiting for the whole time-slice of the thread to finish, it will immediately switch to another thread.

Both your process and your thread have a priority. Assigning a low priority is useful for applications such as a screen saver. Such an application shouldn’t compete with other applications for CPU time. A higher-priority thread should be used only when it’s absolutely necessary. A new thread is assigned a priority of Normal, which is okay for almost all scenarios.

Another thing that’s important to know about threads is the difference between foreground and background threads. Foreground threads can be used to keep an application alive. Only when all foreground threads end does the common language runtime (CLR) shut down your application. Background threads are then terminated.

Example 1-2 shows this difference in action.

Example 1-2. Using a background thread

using System;

using System.Threading;

namespace Chapter1

{

public static class Program

{

public static void ThreadMethod()

{

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

{

Console.WriteLine("ThreadProc: {0}", i);

Thread.Sleep(1000);

}

}

public static void Main()

{

Thread t = new Thread(new ThreadStart(ThreadMethod));

t.IsBackground = true;

t.Start();

}

}

}

If you run this application with the IsBackground property set to true, the application exits immediately. If you set it to false (creating a foreground thread), the application prints the ThreadProc message ten times.

The Thread constructor has another overload that takes an instance of a ParameterizedThreadStart delegate. This overload can be used if you want to pass some data through the start method of your thread to your worker method, as Example 1-3 shows.

Example 1-3. Using the ParameterizedThreadStart

public static void ThreadMethod(object o)

{

for (int i = 0; i < (int)o; i++)

{

Console.WriteLine("ThreadProc: {0}", i);

Thread.Sleep(0);

}

}

public static void Main()

{

Thread t = new Thread(new ParameterizedThreadStart(ThreadMethod));

t.Start(5);

t.Join();

}

In this case, the value 5 is passed to the ThreadMethod as an object. You can cast it to the expected type to use it in your method.

To stop a thread, you can use the Thread.Abort method. However, because this method is executed by another thread, it can happen at any time. When it happens, a ThreadAbort-Exception is thrown on the target thread. This can potentially leave a corrupt state and make your application unusable.

A better way to stop a thread is by using a shared variable that both your target and your calling thread can access. Example 1-4 shows an example.

Example 1-4. Stopping a thread

using System;

using System.Threading;

namespace Chapter1

{

public static class Program

{

public static void ThreadMethod(object o)

{

for (int i = 0; i < (int)o; i++)

{

Console.WriteLine("ThreadProc: {0}", i);

Thread.Sleep(0);

}

}

public static void Main()

{

bool stopped = false;

Thread t = new Thread(new ThreadStart(() =>

{

while (!stopped)

{

Console.WriteLine("Running...");

Thread.Sleep(1000);

}

}));

t.Start();

Console.WriteLine("Press any key to exit");

Console.ReadKey();

stopped = true;

t.Join();

}

}

}

In this case, the thread is initialized with a lambda expression (which in turn is just a shorthand version of a delegate). The thread keeps running until stopped becomes true. After that, the t.Join method causes the console application to wait till the thread finishes execution.

A thread has its own call stack that stores all the methods that are executed. Local variables are stored on the call stack and are private to the thread.

A thread can also have its own data that’s not a local variable. By marking a field with the ThreadStatic attribute, each thread gets its own copy of a field (see Example 1-5).

Example 1-5. Using the ThreadStaticAttribute

using System;

using System.Threading;

namespace Chapter1

{

public static class Program

{

[ThreadStatic]

public static int _field;

public static void Main()

{

new Thread(() =>

{

for(int x = 0; x < 10; x++)

{

_field++;

Console.WriteLine("Thread A: {0}", _field);

}

}).Start();

new Thread(() =>

{

for(int x = 0; x < 10; x++)

{

_field++;

Console.WriteLine("Thread B: {0}", _field);

}

}).Start();

Console.ReadKey();

}

}

}

With the ThreadStaticAttribute applied, the maximum value of _field becomes 10. If you remove it, you can see that both threads access the same value and it becomes 20.

If you want to use local data in a thread and initialize it for each thread, you can use the ThreadLocal<T> class. This class takes a delegate to a method that initializes the value. Example 1-6 shows an example.

Example 1-6. Using ThreadLocal<T>

using System;

using System.Threading;

namespace Chapter1

{

public static class Program

{

public static ThreadLocal<int> _field =

new ThreadLocal<int>(() =>

{

return Thread.CurrentThread.ManagedThreadId;

});

public static void Main()

{

new Thread(() =>

{

for(int x = 0; x < _field.Value; x++)

{

Console.WriteLine("Thread A: {0}", x);

}

}).Start();

new Thread(() =>

{

for (int x = 0; x < _field.Value; x++)

{

Console.WriteLine("Thread B: {0}", x);

}

}).Start();

Console.ReadKey();

}

}

}

// Displays

// Thread B: 0

// Thread B: 1

// Thread B: 2

// Thread B: 3

// Thread A: 0

// Thread A: 1

// Thread A: 2

Here you see another feature of the .NET Framework. You can use the Thread.Current-Thread class to ask for information about the thread that’s executing. This is called the thread’s execution context. This property gives you access to properties like the thread’s current culture (a CultureInfoassociated with the current thread that is used to format dates, times, numbers, currency values, the sorting order of text, casing conventions, and string comparisons), principal (representing the current security context), priority (a value to indicate how the thread should be scheduled by the operating system), and other info.

When a thread is created, the runtime ensures that the initiating thread’s execution context is flowed to the new thread. This way the new thread has the same privileges as the parent thread.

This copying of data does cost some resources, however. If you don’t need this data, you can disable this behavior by using the ExecutionContext.SuppressFlow method.

Thread pools

When working directly with the Thread class, you create a new thread each time, and the thread dies when you’re finished with it. The creation of a thread, however, is something that costs some time and resources.

A thread pool is created to reuse those threads, similar to the way a database connection pooling works. Instead of letting a thread die, you send it back to the pool where it can be reused whenever a request comes in.

When you work with a thread pool from .NET, you queue a work item that is then picked up by an available thread from the pool. Example 1-7 shows how this is done.

Example 1-7. Queuing some work to the thread pool

using System;

using System.Threading;

namespace Chapter1

{

public static class Program

{

public static void Main()

{

ThreadPool.QueueUserWorkItem((s) =>

{

Console.WriteLine("Working on a thread from threadpool");

});

Console.ReadLine();

}

}

}

Because the thread pool limits the available number of threads, you do get a lesser degree of parallelism than using the regular Thread class. But the thread pool also has many advantages.

Take, for example, a web server that serves incoming requests. All those requests come in at an unknown time and frequency. The thread pool ensures that each request gets added to the queue and that when a thread becomes available, it is processed. This ensures that your server doesn’t crash under the amount of requests. If you span threads manually, you can easily bring down your server if you get a lot of requests. Each request has unique characteristics in the work they need to do. What the thread pool does is map this work onto the threads available in the system. Of course, you can still get so many requests that you run out of threads. Requests then start to queue up and this leads to your web server becoming unresponsive.

The thread pool automatically manages the amount of threads it needs to keep around. When it is first created, it starts out empty. As a request comes in, it creates additional threads to handle those requests. As long as it can finish an operation before a new one comes in, no new threads have to be created. If new threads are no longer in use after some time, the thread pool can kill those threads so they no longer use any resources.

MORE INFO: THREAD POOL

For more information on how the thread pool works and how you can configure it, see http://msdn.microsoft.com/en-us/library/system.threading.threadpool.aspx.

One thing to be aware of is that because threads are being reused, they also reuse their local state. You may not rely on state that can potentially be shared between multiple operations.

Using Tasks

Queuing a work item to a thread pool can be useful, but it has its shortcomings. There is no built-in way to know when the operation has finished and what the return value is.

This is why the .NET Framework introduces the concept of a Task, which is an object that represents some work that should be done. The Task can tell you if the work is completed and if the operation returns a result, the Task gives you the result.

A task scheduler is responsible for starting the Task and managing it. By default, the Task scheduler uses threads from the thread pool to execute the Task.

Tasks can be used to make your application more responsive. If the thread that manages the user interface offloads work to another thread from the thread pool, it can keep processing user events and ensure that the application can still be used. But it doesn’t help with scalability. If a thread receives a web request and it would start a new Task, it would just consume another thread from the thread pool while the original thread waits for results.

Executing a Task on another thread makes sense only if you want to keep the user interface thread free for other work or if you want to parallelize your work on to multiple processors.

Example 1-8 shows how to start a new Task and wait until it’s finished.

Example 1-8. Starting a new Task

using System;

using System.Threading.Tasks;

namespace Chapter1

{

public static class Program

{

public static void Main()

{

Task t = Task.Run(() =>

{

for (int x = 0; x < 100; x++)

{

Console.Write('*');

}

});

t.Wait();

}

}

}

This example creates a new Task and immediately starts it. Calling Wait is equivalent to calling Join on a thread. It waits till the Task is finished before exiting the application.

Next to Task, the .NET Framework also has the Task<T> class that you can use if a Task should return a value. Example 1-9 shows how this works.

Example 1-9. Using a Task that returns a value.

using System;

using System.Threading.Tasks;

namespace Chapter1

{

public static class Program

{

public static void Main()

{

Task<int> t = Task.Run(() =>

{

return 42;

});

Console.WriteLine(t.Result); // Displays 42

}

}

}

Attempting to read the Result property on a Task will force the thread that’s trying to read the result to wait until the Task is finished before continuing. As long as the Task has not finished, it is impossible to give the result. If the Task is not finished, this call will block the current thread.

Because of the object-oriented nature of the Task object, one thing you can do is add a continuation task. This means that you want another operation to execute as soon as the Task finishes.

Example 1-10 shows an example of creating such a continuation.

Example 1-10. Adding a continuation

Task<int> t = Task.Run(() =>

{

return 42;

}).ContinueWith((i) =>

{

return i.Result * 2;

});

Console.WriteLine(t.Result); // Displays 84

The ContinueWith method has a couple of overloads that you can use to configure when the continuation will run. This way you can add different continuation methods that will run when an exception happens, the Task is canceled, or the Task completes successfully. Example 1-11 shows how to do this.

Example 1-11. Scheduling different continuation tasks

Task<int> t = Task.Run(() =>

{

return 42;

});

t.ContinueWith((i) =>

{

Console.WriteLine("Canceled");

}, TaskContinuationOptions.OnlyOnCanceled);

t.ContinueWith((i) =>

{

Console.WriteLine("Faulted");

}, TaskContinuationOptions.OnlyOnFaulted);

var completedTask = t.ContinueWith((i) =>

{

Console.WriteLine("Completed");

}, TaskContinuationOptions.OnlyOnRanToCompletion);

completedTask.Wait();

Next to continuation Tasks, a Task can also have several child Tasks. The parent Task finishes when all the child tasks are ready. Example 1-12 shows how this works.

Example 1-12. Attaching child tasks to a parent task

using System;

using System.Threading.Tasks;

namespace Chapter1

{

public static class Program

{

public static void Main()

{

Task<Int32[]> parent = Task.Run(() =>

{

var results = new Int32[3];

new Task(() => results[0] = 0,

TaskCreationOptions.AttachedToParent).Start();

new Task(() => results[1] = 1,

TaskCreationOptions.AttachedToParent).Start();

new Task(() => results[2] = 2,

TaskCreationOptions.AttachedToParent).Start();

return results;

});

var finalTask = parent.ContinueWith(

parentTask => {

foreach(int i in parentTask.Result)

Console.WriteLine(i);

});

finalTask.Wait();

}

}

}

The finalTask runs only after the parent Task is finished, and the parent Task finishes when all three children are finished. You can use this to create quite complex Task hierarchies that will go through all the steps you specified.

In the previous example, you had to create three Tasks all with the same options. To make the process easier, you can use a TaskFactory. A TaskFactory is created with a certain configuration and can then be used to create Tasks with that configuration. Example 1-13 shows how you can simplify the previous example with a factory.

Example 1-13. Using a TaskFactory

using System;

using System.Threading.Tasks;

namespace Chapter1

{

public static class Program

{

public static void Main()

{

Task<Int32[]> parent = Task.Run(() =>

{

var results = new Int32[3];

TaskFactory tf = new TaskFactory(TaskCreationOptions.AttachedToParent,

TaskContinuationOptions.ExecuteSynchronously);

tf.StartNew(() => results[0] = 0);

tf.StartNew(() => results[1] = 1);

tf.StartNew(() => results[2] = 2);

return results;

});

var finalTask = parent.ContinueWith(

parentTask => {

foreach(int i in parentTask.Result)

Console.WriteLine(i);

});

finalTask.Wait();

}

}

}

Next to calling Wait on a single Task, you can also use the method WaitAll to wait for multiple Tasks to finish before continuing execution. Example 1-14 shows how to use this.

Example 1-14. Using Task.WaitAll

using System.Threading;

using System.Threading.Tasks;

namespace Chapter1

{

public static class Program

{

public static void Main()

{

Task[] tasks = new Task[3];

tasks[0] = Task.Run(() => {

Thread.Sleep(1000);

Console.WriteLine("1");

return 1;

});

tasks[1] = Task.Run(() => {

Thread.Sleep(1000);

Console.WriteLine("2");

return 2;

});

tasks[2] = Task.Run(() => {

Thread.Sleep(1000);

Console.WriteLine("3");

return 3; }

);

Task.WaitAll(tasks);

}

}

}

In this case, all three Tasks are executed simultaneously, and the whole run takes approximately 1000ms instead of 3000. Next to WaitAll, you also have a WhenAll method that you can use to schedule a continuation method after all Tasks have finished.

Instead of waiting until all tasks are finished, you can also wait until one of the tasks is finished. You use the WaitAny method for this. Example 1-15 shows how this works.

Example 1-15. Using Task.WaitAny

using System;

using System.Linq;

using System.Threading;

using System.Threading.Tasks;

namespace Chapter1

{

public static class Program

{

public static void Main()

{

Task<int>[] tasks = new Task<int>[3];

tasks[0] = Task.Run(() => { Thread.Sleep(2000); return 1; });

tasks[1] = Task.Run(() => { Thread.Sleep(1000); return 2; });

tasks[2] = Task.Run(() => { Thread.Sleep(3000); return 3; });

while (tasks.Length > 0)

{

int i = Task.WaitAny(tasks);

Task<int> completedTask = tasks[i];

Console.WriteLine(completedTask.Result);

var temp = tasks.ToList();

temp.RemoveAt(i);

tasks = temp.ToArray();

}

}

}

}

In this example, you process a completed Task as soon as it finishes. By keeping track of which Tasks are finished, you don’t have to wait until all Tasks have completed.

Using the Parallel class

The System.Threading.Tasks namespace also contains another class that can be used for parallel processing. The Parallel class has a couple of static methods—For, ForEach, and Invoke—that you can use to parallelize work.

Parallelism involves taking a certain task and splitting it into a set of related tasks that can be executed concurrently. This also means that you shouldn’t go through your code to replace all your loops with parallel loops. You should use the Parallel class only when your code doesn’t have to be executed sequentially.

Increasing performance with parallel processing happens only when you have a lot of work to be done that can be executed in parallel. For smaller work sets or for work that has to synchronize access to resources, using the Parallel class can hurt performance.

The best way to know whether it will work in your situation is to measure the results.

Example 1-16 shows an example of using Parallel.For and Parallel.ForEach.

Example 1-16. Using Parallel.For and Parallel.Foreach

Parallel.For(0, 10, i =>

{

Thread.Sleep(1000);

});

var numbers = Enumerable.Range(0, 10);

Parallel.ForEach(numbers, i =>

{

Thread.Sleep(1000);

});

You can cancel the loop by using the ParallelLoopState object. You have two options to do this: Break or Stop. Break ensures that all iterations that are currently running will be finished. Stop just terminates everything. Example 1-17 shows an example.

Example 1-17. Using Parallel.Break

ParallelLoopResult result = Parallel.

For(0, 1000, (int i, ParallelLoopState loopState) =>

{

if (i == 500)

{

Console.WriteLine("Breaking loop");

loopState.Break();

}

return;

});

When breaking the parallel loop, the result variable has an IsCompleted value of false and a LowestBreakIteration of 500. When you use the Stop method, the LowestBreakIteration is null.

Using async and await

As you have seen, long-running CPU-bound tasks can be handed to another thread by using the Task object. But when doing work that’s input/output (I/O)–bound, things go a little differently.

When your application is executing an I/O operation on the primary application thread, Windows notices that your thread is waiting for the I/O operation to complete. Maybe you are accessing some file on disk or over the network, and this could take some time.

Because of this, Windows pauses your thread so that it doesn’t use any CPU resources. But while doing this, it still uses memory, and the thread can’t be used to serve other requests, which in turn will lead to new threads being created if requests come in.

Asynchronous code solves this problem. Instead of blocking your thread until the I/O operation finishes, you get back a Task object that represents the result of the asynchronous operation. By setting a continuation on this Task, you can continue when the I/O is done. In the meantime, your thread is available for other work. When the I/O operation finishes, Windows notifies the runtime and the continuation Task is scheduled on the thread pool.

But writing asynchronous code is not easy. You have to make sure that all edge cases are handled and that nothing can go wrong. Because of this predicament, C# 5 has added two new keywords to simplify writing asynchronous code. Those keywords are async and await.

You use the async keyword to mark a method for asynchronous operations. This way, you signal to the compiler that something asynchronous is going to happen. The compiler responds to this by transforming your code into a state machine.

A method marked with async just starts running synchronously on the current thread. What it does is enable the method to be split into multiple pieces. The boundaries of these pieces are marked with the await keyword.

When you use the await keyword, the compiler generates code that will see whether your asynchronous operation is already finished. If it is, your method just continues running synchronously. If it’s not yet completed, the state machine will hook up a continuation method that should run when the Task completes. Your method yields control to the calling thread, and this thread can be used to do other work.

Example 1-18 shows a simple example of an asynchronous method.

Example 1-18. async and await

using System;

using System.Net.Http;

using System.Threading.Tasks;

namespace Chapter1.Threads

{

public static class Program

{

public static void Main()

{

string result = DownloadContent().Result;

Console.WriteLine(result);

}

public static async Task<string> DownloadContent()

{

using(HttpClient client = new HttpClient())

{

string result = await client.GetStringAsync("http://www.microsoft.com");

return result;

}

}

}

}

Because the entry method of an application can’t be marked as async, the example uses the Wait method in Main. This class uses both the async and await keywords in the DownloadContent method.

The GetStringAsync uses asynchronous code internally and returns a Task<string> to the caller that will finish when the data is retrieved. In the meantime, your thread can do other work.

The nice thing about async and await is that they let the compiler do the thing it’s best at: generate code in precise steps. Writing correct asynchronous code by hand is difficult, especially when trying to implement exception handling. Doing this correctly can become difficult quickly. Adding continuation tasks also breaks the logical flow of the code. Your code doesn’t read top to bottom anymore. Instead, program flow jumps around, and it’s harder to follow when debugging your code. The await keyword enables you to write code that looks synchronous but behaves in an asynchronous way. The Visual Studio debugger is even clever enough to help you in debugging asynchronous code as if it were synchronous.

So doing a CPU-bound task is different from an I/O-bound task. CPU-bound tasks always use some thread to execute their work. An asynchronous I/O-bound task doesn’t use a thread until the I/O is finished.

If you are building a client application that needs to stay responsive while background operations are running, you can use the await keyword to offload a long-running operation to another thread. Although this does not improve performance, it does improve responsiveness. The awaitkeyword also makes sure that the remainder of your method runs on the correct user interface thread so you can update the user interface.

Making a scalable application that uses fewer threads is another story. Making code scale better is about changing the actual implementation of the code. Example 1-19 shows an example of this.

Example 1-19. Scalability versus responsiveness

public Task SleepAsyncA(int millisecondsTimeout)

{

return Task.Run(() => Thread.Sleep(millisecondsTimeout));

}

public Task SleepAsyncB(int millisecondsTimeout)

{

TaskCompletionSource<bool> tcs = null;

var t = new Timer(delegate { tcs.TrySetResult(true); }, null, -1, -1);

tcs = new TaskCompletionSource<bool>(t);

t.Change(millisecondsTimeout, -1);

return tcs.Task;

}

The SleepAsyncA method uses a thread from the thread pool while sleeping. The second method, however, which has a completely different implementation, does not occupy a thread while waiting for the timer to run. The second method gives you scalability.

When using the async and await keywords, you should keep this in mind. Just wrapping each and every operation in a task and awaiting them won’t make your application perform any better. It could, however, improve responsiveness, which is very important in client applications.

The FileStream class, for example, exposes asynchronous methods such as WriteAsync and ReadAsync. They use an implementation that makes use of actual asynchronous I/O. This way, they don’t use a thread while they are waiting on the hard drive of your system to read or write some data.

When an exception happens in an asynchronous method, you normally expect an AggregateException. However, the generated code helps you unwrap the AggregateException and throws the first of its inner exceptions. This makes the code more intuitive to use and easier to debug.

One other thing that’s important when working with asynchronous code is the concept of a SynchronizationContext, which connects its application model to its threading model. For example, a WPF application uses a single user interface thread and potentially multiple background threads to improve responsiveness and distribute work across multiple CPUs. An ASP.NET application, however, uses threads from the thread pool that are initialized with the correct data, such as current user and culture to serve incoming requests.

The SynchronizationContext abstracts the way these different applications work and makes sure that you end up on the right thread when you need to update something on the UI or process a web request.

The await keyword makes sure that the current SynchronizationContext is saved and restored when the task finishes. When using await inside a WPF application, this means that after your Task finishes, your program continues running on the user interface thread. In an ASP.NET application, the remaining code runs on a thread that has the client’s cultural, principal, and other information set.

If you want, you can disable the flow of the SynchronizationContext. Maybe your continuation code can run on any thread because it doesn’t need to update the UI after it’s finished. By disabling the SynchronizationContext, your code performs better. Example 1-20 shows an example of a button event handler in a WPF application that downloads a website and then puts the result in a label.

Example 1-20. Using ConfigureAwait

private async void Button_Click(object sender, RoutedEventArgs e)

{

HttpClient httpClient = new HttpClient();

string content = await httpClient

.GetStringAsync("http://www.microsoft.com")

.ConfigureAwait(false);

Output.Content = content;

}

This example throws an exception; the Output.Content line is not executed on the UI thread because of the ConfigureAwait(false). If you do something else, such as writing the content to file, you don’t need to set the SynchronizationContext to be set (see Example 1-21).

Example 1-21. Continuing on a thread pool instead of the UI thread

private async void Button_Click(object sender, RoutedEventArgs e)

{

HttpClient httpClient = new HttpClient();

string content = await httpClient

.GetStringAsync("http://www.microsoft.com")

.ConfigureAwait(false);

using (FileStream sourceStream = new FileStream("temp.html",

FileMode.Create, FileAccess.Write, FileShare.None,

4096, useAsync: true))

{

byte[] encodedText = Encoding.Unicode.GetBytes(content);

await sourceStream.WriteAsync(encodedText, 0, encodedText.Length)

.ConfigureAwait(false);

};

}

Both awaits use the ConfigureAwait(false) method because if the first method is already finished before the awaiter checks, the code still runs on the UI thread.

When creating async methods, it’s important to choose a return type of Task or Task<T>. Avoid the void return type. A void returning async method is effectively a fire-and-forget method. You can never inspect the return type, and you can’t see whether any exceptions were thrown. You should use async void methods only when dealing with asynchronous events.

The use of the new async/await keywords makes it much easier to write asynchronous code. In today’s world with multiple cores and requirements for responsiveness and scalability, it’s important to look for opportunities to use these new keywords to improve your applications.

EXAM TIP

When using async and await keep in mind that you should never have a method marked async without any await statements. You should also avoid returning void from an async method except when it’s an event handler.

Using Parallel Language Integrated Query (PLINQ)

Language-Integrated Query (LINQ) is a popular addition to the C# language. You can use it to perform queries over all kinds of data.

Parallel Language-Integrated Query (PLINQ) can be used on objects to potentially turn a sequential query into a parallel one.

Extension methods for using PLINQ are defined in the System.Linq.ParallelEnumerable class. Parallel versions of LINQ operators, such as Where, Select, SelectMany, GroupBy, Join, OrderBy, Skip, and Take, can be used.

Example 1-22 shows how you can convert a query to a parallel query.

Example 1-22. Using AsParallel

var numbers = Enumerable.Range(0, 100000000);

var parallelResult = numbers.AsParallel()

.Where(i => i % 2 == 0)

.ToArray();

The runtime determines whether it makes sense to turn your query into a parallel one. When doing this, it generates Task objects and starts executing them. If you want to force PLINQ into a parallel query, you can use the WithExecutionMode method and specify that it should always execute the query in parallel.

You can also limit the amount of parallelism that is used with the WithDegreeOfParallelism method. You pass that method an integer that represents the number of processors that you want to use. Normally, PLINQ uses all processors (up to 64), but you can limit it with this method if you want.

One thing to keep in mind is that parallel processing does not guarantee any particular order. Example 1-23 shows what can happen.

Example 1-23. Unordered parallel query

using System;

using System.Linq;

namespace Chapter1

{

public static class Program

{

public static void Main()

{

var numbers = Enumerable.Range(0, 10);

var parallelResult = numbers.AsParallel()

.Where(i => i % 2 == 0)

.ToArray();

foreach (int i in parallelResult)

Console.WriteLine(i);

}

}

}

// Displays

// 2

// 0

// 4

// 6

// 8

As you can see, the returned results from this query are in no particular order. The results of this code vary depending on the amount of CPUs that are available. If you want to ensure that the results are ordered, you can add the AsOrdered operator. Your query is still processed in parallel, but the results are buffered and sorted. Example 1-24 shows how this works.

Example 1-24. Ordered parallel query

using System;

using System.Linq;

namespace Chapter1

{

public static class Program

{

public static void Main()

{

var numbers = Enumerable.Range(0, 10);

var parallelResult = numbers.AsParallel().AsOrdered()

.Where(i => i % 2 == 0)

.ToArray();

foreach (int i in parallelResult)

Console.WriteLine(i);

}

}

}

// Displays

// 0

// 2

// 4

// 6

// 8

If you have a complex query that can benefit from parallel processing but also has some parts that should be done sequentially, you can use the AsSequential to stop your query from being processed in parallel.

One scenario where this is required is to preserve the ordering of your query. Example 1-25 shows how you can use the AsSequential operator to make sure that the Take method doesn’t mess up your order.

Example 1-25. Making a parallel query sequential

var numbers = Enumerable.Range(0, 20);

var parallelResult = numbers.AsParallel().AsOrdered()

.Where(i => i % 2 == 0).AsSequential();

foreach (int i in parallelResult.Take(5))

Console.WriteLine(i);

// Displays

// 0

// 2

// 4

// 6

// 8

When using PLINQ, you can use the ForAll operator to iterate over a collection when the iteration can also be done in a parallel way. Example 1-26 shows how to do this.

Example 1-26. Using ForAll

var numbers = Enumerable.Range(0, 20);

var parallelResult = numbers.AsParallel()

.Where(i => i % 2 == 0);

parallelResult.ForAll(e => Console.WriteLine(e));

In contrast to foreach, ForAll does not need all results before it starts executing. In this example, ForAll does, however, remove any sort order that is specified.

Of course, it can happen that some of the operations in your parallel query throw an exception. The .NET Framework handles this by aggregating all exceptions into one AggregateException. This exception exposes a list of all exceptions that have happened during parallel execution. Example 1-27 shows how you can handle this.

Example 1-27. Catching AggregateException

using System;

using System.Linq;

namespace Chapter1

{

public static class Program

{

public static void Main()

{

var numbers = Enumerable.Range(0, 20);

try

{

var parallelResult = numbers.AsParallel()

.Where(i => IsEven(i));

parallelResult.ForAll(e => Console.WriteLine(e));

}

catch (AggregateException e)

{

Console.WriteLine("There where {0} exceptions",

e.InnerExceptions.Count);

}

}

public static bool IsEven(int i)

{

if (i % 10 == 0) throw new ArgumentException("i");

return i % 2 == 0;

}

}

}

// Displays

// 4

// 6

// 8

// 2

// 12

// 14

// 16

// 18

// There where 2 exceptions

As you can see, two exceptions were thrown while processing the data. You can inspect those exceptions by looping through the InnerExceptions property.

Using concurrent collections

When working in a multithreaded environment, you need to make sure that you are not manipulating shared data at the same time without synchronizing access.

The .NET Framework offers some collection classes that are created specifically for use in concurrent environments, which is what you have when you’re using multithreading. These collections are thread-safe, which means that they internally use synchronization to make sure that they can be accessed by multiple threads at the same time.

Those collections are the following:

§ BlockingCollection<T>

§ ConcurrentBag<T>

§ ConcurrentDictionary<TKey,T>

§ ConcurrentQueue<T>

§ ConcurrentStack<T>

BlockingCollection<T>

This collection is thread-safe for adding and removing data. Removing an item from the collection can be blocked until data becomes available. Adding data is fast, but you can set a maximum upper limit. If that limit is reached, adding an item blocks the calling thread until there is room.

BlockingCollection is in reality a wrapper around other collection types. If you don’t give it any specific instructions, it uses the ConcurrentQueue by default.

A regular collection blows up when being used in a multithreaded scenario because an item might be removed by one thread while the other thread is trying to read it.

Example 1-28 shows an example of using a BlockingCollection. One Task listens for new items being added to the collection. It blocks if there are no items available. The other Task adds items to the collection.

Example 1-28. Using BlockingCollection<T>

using System;

using System.Collections.Concurrent;

using System.Threading.Tasks;

namespace Chapter1

{

public static class Program

{

public static void Main()

{

BlockingCollection<string> col = new BlockingCollection<string>();

Task read = Task.Run(() =>

{

while (true)

{

Console.WriteLine(col.Take());

}

});

Task write = Task.Run(() =>

{

while (true)

{

string s = Console.ReadLine();

if (string.IsNullOrWhiteSpace(s)) break;

col.Add(s);

}

});

write.Wait();

}

}

}

The program terminates when the user doesn’t enter any data. Until that, every string entered is added by the write Task and removed by the read Task.

You can use the CompleteAdding method to signal to the BlockingCollection that no more items will be added. If other threads are waiting for new items, they won’t be blocked anymore.

You can even remove the while(true) statements from Example 1-28. By using the GetConsumingEnumerable method, you get an IEnumerable that blocks until it finds a new item. That way, you can use a foreach with your BlockingCollection to enumerate it (see Example 1-29).

Example 1-29. Using GetConsumingEnumerable on a BlockingCollection

Task read = Task.Run(() =>

{

foreach (string v in col.GetConsumingEnumerable())

Console.WriteLine(v);

});

MORE INFO: IENUMERABLE

For more information about using IEnumerable, see Chapter 2.

ConcurrentBag

A ConcurrentBag is just a bag of items. It enables duplicates and it has no particular order. Important methods are Add, TryTake, and TryPeek.

Example 1-30 shows how to work with the ConcurrentBag.

Example 1-30. Using a ConcurrentBag

ConcurrentBag<int> bag = new ConcurrentBag<int>();

bag.Add(42);

bag.Add(21);

int result;

if (bag.TryTake(out result))

Console.WriteLine(result);

if (bag.TryPeek(out result))

Console.WriteLine("There is a next item: {0}", result);

One thing to keep in mind is that the TryPeek method is not very useful in a multithreaded environment. It could be that another thread removes the item before you can access it.

ConcurrentBag also implements IEnumerable<T>, so you can iterate over it. This operation is made thread-safe by making a snapshot of the collection when you start iterating it, so items added to the collection after you started iterating it won’t be visible. Example 1-31 shows this in practice.

Example 1-31. Enumerating a ConcurrentBag

ConcurrentBag<int> bag = new ConcurrentBag<int>();

Task.Run(() =>

{

bag.Add(42);

Thread.Sleep(1000);

bag.Add(21);

});

Task.Run(() =>

{

foreach (int i in bag)

Console.WriteLine(i);

}).Wait();

// Displays

// 42

This code only displays 42 because the other value is added after iterating over the bag has started.

ConcurrentStack and ConcurrentQueue

A stack is a last in, first out (LIFO) collection. A queue is a first in, first out (FIFO) collection.

ConcurrentStack has two important methods: Push and TryPop. Push is used to add an item to the stack; TryPop tries to get an item off the stack. You can never be sure whether there are items on the stack because multiple threads might be accessing your collection at the same time.

You can also add and remove multiple items at once by using PushRange and TryPopRange. When you enumerate the collection, a snapshot is taken.

Example 1-32 shows how these methods work.

Example 1-32. Using a ConcurrentStack

ConcurrentStack<int> stack = new ConcurrentStack<int>();

stack.Push(42);

int result;

if (stack.TryPop(out result))

Console.WriteLine("Popped: {0}", result);

stack.PushRange(new int[] { 1, 2, 3 });

int[] values = new int[2];

stack.TryPopRange(values);

foreach (int i in values)

Console.WriteLine(i);

// Popped: 42

// 3

// 2

ConcurrentQueue offers the methods Enqueue and TryDequeue to add and remove items from the collection. It also has a TryPeek method and it implements IEnumerable by making a snapshot of the data. Example 1-33 shows how to use a ConcurrentQueue.

Example 1-33. Using a ConcurrentQueue.

ConcurrentQueue<int> queue = new ConcurrentQueue<int>();

queue.Enqueue(42);

int result;

if (queue.TryDequeue(out result))

Console.WriteLine("Dequeued: {0}", result);

// Dequeued: 42

ConcurrentDictionary

A ConcurrentDictionary stores key and value pairs in a thread-safe manner. You can use methods to add and remove items, and to update items in place if they exist.

Example 1-34 shows the methods that you can use on a ConcurrentDictionary.

Example 1-34. Using a ConcurrentDictionary

var dict = new ConcurrentDictionary<string, int>();

if (dict.TryAdd("k1", 42))

{

Console.WriteLine("Added");

}

if (dict.TryUpdate("k1", 21, 42))

{

Console.WriteLine("42 updated to 21");

}

dict["k1"] = 42; // Overwrite unconditionally

int r1 = dict.AddOrUpdate("k1", 3, (s, i) => i * 2);

int r2 = dict.GetOrAdd("k2", 3);

When working with a ConcurrentDictionary you have methods that can atomically add, get, and update items. An atomic operation means that it will be started and finished as a single step without other threads interfering. TryUpdate checks to see whether the current value is equal to the existing value before updating it. AddOrUpdate makes sure an item is added if it’s not there, and updated to a new value if it is. GetOrAdd gets the current value of an item if it’s available; if not, it adds the new value by using a factory method.

THOUGHT EXPERIMENT

Implementing multithreading

In this thought experiment, apply what you’ve learned about this objective. You can find answers to these questions in the Answers section at the end of this chapter.

You need to build a new application, and you look into multithreading capabilities. Your application consists of a client application that communicates with a web server.

1. Explain how multithreading can help with your client application.

2. What is the difference between CPU and I/O bound operations?

3. Does using multithreading with the TPL offer the same advantages for your server application?

Objective summary

§ A thread can be seen as a virtualized CPU.

§ Using multiple threads can improve responsiveness and enables you to make use of multiple processors.

§ The Thread class can be used if you want to create your own threads explicitly. Otherwise, you can use the ThreadPool to queue work and let the runtime handle things.

§ A Task object encapsulates a job that needs to be executed. Tasks are the recommended way to create multithreaded code.

§ The Parallel class can be used to run code in parallel.

§ PLINQ is an extension to LINQ to run queries in parallel.

§ The new async and await operators can be used to write asynchronous code more easily.

§ Concurrent collections can be used to safely work with data in a multithreaded (concurrent access) environment.

Objective review

Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the Answers section at the end of this chapter.

1. You have a lot of items that need to be processed. For each item, you need to perform a complex calculation. Which technique should you use?

a. You create a Task for each item and then wait until all tasks are finished.

b. You use Parallel.For to process all items concurrently.

c. You use async/await to process all items concurrently.

d. You add all items to a BlockingCollection and process them on a thread created by the Thread class.

2. You are creating a complex query that doesn’t require any particular order and you want to run it in parallel. Which method should you use?

a. AsParallel

b. AsSequential

c. AsOrdered

d. WithDegreeOfParallelism

3. You are working on an ASP.NET application that retrieves some data from another web server and then writes the response to the database. Should you use async/await?

a. No, both operations depend on external factors. You need to wait before they are finished.

b. No, in a server application you don’t have to use async/await. It’s only for responsiveness on the client.

c. Yes, this will free your thread to serve other requests while waiting for the I/O to complete.

d. Yes, this put your thread to sleep while waiting for I/O so that it doesn’t use any CPU.

Objective 1.2: Manage multithreading

Although multithreading can give you a lot of advantages, it’s not easy to write a multithreaded application. Problems can happen when different threads access some shared data. What should happen when both try to change something at the same time? To make this work successfully,synchronizing resources is important.

THIS OBJECTIVE COVERS HOW TO:

§ Synchronize resources.

§ Cancel long-running tasks.

Synchronizing resources

As you have seen, with the TPL support in .NET, it’s quite easy to create a multithreaded application. But when you build real-world applications with multithreading, you run into problems when you want to access the same data from multiple threads simultaneously. Example 1-35 shows an example of what can go wrong.

Example 1-35. Accessing shared data in a multithreaded application

using System;

using System.Threading.Tasks;

namespace Chapter1

{

public class Program

{

static void Main()

{

int n = 0;

var up = Task.Run(() =>

{

for (int i = 0; i < 1000000; i++)

n++;

});

for (int i = 0; i < 1000000; i++)

n--;

up.Wait();

Console.WriteLine(n);

}

}

}

What would the output of Example 1-35 be? The answer is, it depends. When you run this application, you get a different output each time. The seemingly simple operation of incrementing and decrementing the variable n results in both a lookup (check the value of n) and add or subtract 1 from n. But what if the first task reads the value and adds 1, and at the exact same time task 2 reads the value and subtracts 1? This is what happens in this example and that’s why you never get the expected output of 0.

This is because the operation is not atomic. It consists of both a read and a write that happen at different moments. This is why access to the data you’re working with needs to be synchronized, so you can reliably predict how your data is affected.

It’s important to synchronize access to shared data. One feature the C# language offers is the lock operator, which is some syntactic sugar that the compiler translates in a call to System.Thread.Monitor. Example 1-36 shows the use of the lock operator to fix the previous example.

Example 1-36. Using the lock keyword

using System;

using System.Threading.Tasks;

namespace Chapter1

{

public class Program

{

static void Main()

{

int n = 0;

object _lock = new object();

var up = Task.Run(() =>

{

for (int i = 0; i < 1000000; i++)

lock (_lock)

n++;

});

for (int i = 0; i < 1000000; i++)

lock (_lock)

n--;

up.Wait();

Console.WriteLine(n);

}

}

}

After this change, the program always outputs 0 because access to the variable n is now synchronized. There is no way that one thread could change the value while the other thread is working with it.

However, it also causes the threads to block while they are waiting for each other. This can give performance problems and it could even lead to a deadlock, where both threads wait on each other, causing neither to ever complete. Example 1-37 shows an example of a deadlock.

Example 1-37. Creating a deadlock

using System;

using System.Threading;

using System.Threading.Tasks;

namespace Chapter1

{

public class Program

{

static void Main()

{

object lockA = new object();

object lockB = new object();

var up = Task.Run(() =>

{

lock (lockA)

{

Thread.Sleep(1000);

lock (lockB)

{

Console.WriteLine("Locked A and B");

}

}

});

lock (lockB)

{

lock (lockA)

{

Console.WriteLine("Locked A and B");

}

}

up.Wait();

}

}

}

Because both locks are taken in reverse order, a deadlock occurs. The first Task locks A and waits for B to become free. The main thread, however, has B locked and is waiting for A to be released.

You need to be careful to avoid deadlocks in your code. You can avoid a deadlock by making sure that locks are requested in the same order. That way, the first thread can finish its work, after which the second thread can continue.

The lock code is translated by the compiler into something that looks like Example 1-38.

Example 1-38. Generated code from a lock statement

object gate = new object();

bool __lockTaken = false;

try

{

Monitor.Enter(gate, ref __lockTaken);

}

finally

{

if (__lockTaken)

Monitor.Exit(gate);

}

You shouldn’t write this code by hand; let the compiler generate it for you. The compiler takes care of tricky edge cases that can happen.

It’s important to use the lock statement with a reference object that is private to the class. A public object could be used by other threads to acquire a lock without your code knowing.

It should also be a reference type because a value type would get boxed each time you acquired a lock. In practice, this generates a completely new lock each time, losing the locking mechanism. Fortunately, the compiler helps by raising an error when you accidentally use a value type for thelock statement.

You should also avoid locking on the this variable because that variable could be used by other code to create a lock, causing deadlocks.

For the same reason, you should not lock on a string. Because of string-interning (the process in which the compiler creates one object for several strings that have the same content) you could suddenly be asking for a lock on an object that is used in multiple places.

Volatile class

The C# compiler is pretty good at optimizing code. The compiler can even remove complete statements if it discovers that certain code would never be executed.

The compiler sometimes changes the order of statements in your code. Normally, this wouldn’t be a problem in a single-threaded environment. But take a look at Example 1-39, in which a problem could happen in a multithreaded environment.

Example 1-39. A potential problem with multithreaded code

private static int _flag = 0;

private static int _value = 0;

public static void Thread1()

{

_value = 5;

_flag = 1;

}

public static void Thread2()

{

if (_flag == 1)

Console.WriteLine(_value);

}

Normally, if you would run Thread1 and Thread2, you would expect no output or an output of 5. It could be, however, that the compiler switches the two lines in Thread1. If Thread2 then executes, it could be that _flag has a value of 1 and _value has a value of 0.

You can use locking to fix this, but there is also another class in the .NET Framework that you can use: System.Threading.Volatile. This class has a special Write and Read method, and those methods disable the compiler optimizations so you can force the correct order in your code. Using these methods in the correct order can be quite complex, so .NET offers the volatile keyword that you can apply to a field. You would then change the declaration of your field to this:

private static volatile int _flag = 0;

It’s good to be aware of the existence of the volatile keyword, but it’s something you should use only if you really need it. Because it disables certain compiler optimizations, it will hurt performance. It’s also not something that is supported by all .NET languages (Visual Basic doesn’t support it), so it hinders language interoperability.

The Interlocked class

Referring to Example 1-35, the essential problem was that the operations of adding and subtracting were not atomic. This because n++ is translated into n = n + 1, both a read and a write.

Making operations atomic is the job of the Interlocked class that can be found in the System.Threading namespace. When using the Interlocked.Increment and Interlocked.Decrement, you create an atomic operation, as Example 1-40 shows.

Example 1-40. Using the Interlocked class

using System;

using System.Threading;

using System.Threading.Tasks;

namespace Chapter1

{

public class Program

{

static void Main()

{

int n = 0;

var up = Task.Run(() =>

{

for (int i = 0; i < 1000000; i++)

Interlocked.Increment(ref n);

});

for (int i = 0; i < 1000000; i++)

Interlocked.Decrement(ref n);

up.Wait();

Console.WriteLine(n);

}

}

}

Interlocked guarantees that the increment and decrement operations are executed atomically. No other thread will see any intermediate results. Of course, adding and subtracting is a simple operation. If you have more complex operations, you would still have to use a lock.

Interlocked also supports switching values by using the Exchange method. You use this method as follows:

if ( Interlocked.Exchange(ref isInUse, 1) == 0) { }

This code retrieves the current value and immediately sets it to the new value in the same operation. It returns the previous value before changing it.

You can also use the CompareExchange method. This method first checks to see whether the expected value is there; if it is, it replaces it with another value.

Example 1-41 shows what can go wrong when comparing and exchanging a value in a nonatomic operation.

Example 1-41. Compare and exchange as a nonatomic operation

using System;

using System.Threading;

using System.Threading.Tasks;

public static class Program

{

static int value = 1;

public static void Main()

{

Task t1 = Task.Run(() =>

{

if (value == 1)

{

// Removing the following line will change the output

Thread.Sleep(1000);

value = 2;

}

});

Task t2 = Task.Run(() =>

{

value = 3;

});

Task.WaitAll(t1, t2);

Console.WriteLine(value); // Displays 2

}

}

Task t1 starts running and sees that value is equal to 1. At the same time, t2 changes the value to 3 and then t1 changes it back to 2. To avoid this, you can use the following Interlocked statement:

Interlocked.CompareExchange(ref value, newValue, compareTo);

This makes sure that comparing the value and exchanging it for a new one is an atomic operation. This way, no other thread can change the value between comparing and exchanging it.

Canceling tasks

When working with multithreaded code such as the TPL, the Parallel class, or PLINQ, you often have long-running tasks. The .NET Framework offers a special class that can help you in canceling these tasks: CancellationToken.

You pass a CancellationToken to a Task, which then periodically monitors the token to see whether cancellation is requested.

Example 1-42 shows how you can use a CancellationToken to end a task.

Example 1-42. Using a CancellationToken

CancellationTokenSource cancellationTokenSource =

new CancellationTokenSource();

CancellationToken token = cancellationTokenSource.Token;

Task task = Task.Run(() =>

{

while(!token.IsCancellationRequested)

{

Console.Write("*");

Thread.Sleep(1000);

}

}, token);

Console.WriteLine("Press enter to stop the task");

Console.ReadLine();

cancellationTokenSource.Cancel();

Console.WriteLine("Press enter to end the application");

Console.ReadLine();

The CancellationToken is used in the asynchronous Task. The CancellationTokenSource is used to signal that the Task should cancel itself.

In this case, the operation will just end when cancellation is requested. Outside users of the Task won’t see anything different because the Task will just have a RanToCompletion state. If you want to signal to outside users that your task has been canceled, you can do this by throwing anOperationCanceledException. Example 1-43 shows how to do this.

Example 1-43. Throwing OperationCanceledException

using System;

using System.Threading;

using System.Threading.Tasks;

namespace Chapter1.Threads

{

public class Program

{

static void Main()

{

CancellationTokenSource cancellationTokenSource =

new CancellationTokenSource();

CancellationToken token = cancellationTokenSource.Token;

Task task = Task.Run(() =>

{

while (!token.IsCancellationRequested)

{

Console.Write("*");

Thread.Sleep(1000);

}

token.ThrowIfCancellationRequested();

}, token);

try

{

Console.WriteLine("Press enter to stop the task");

Console.ReadLine();

cancellationTokenSource.Cancel();

task.Wait();

}

catch (AggregateException e)

{

Console.WriteLine(e.InnerExceptions[0].Message);

}

Console.WriteLine("Press enter to end the application");

Console.ReadLine();

}

}

}

// Displays

// Press enter to stop the task

// **

// A task was canceled.

// Press enter to end the application

Instead of catching the exception, you can also add a continuation Task that executes only when the Task is canceled. In this Task, you have access to the exception that was thrown, and you can choose to handle it if that’s appropriate. Example 1-44 shows what such a continuation task would look like.

Example 1-44. Adding a continuation for canceled tasks

Task task = Task.Run(() =>

{

while (!token.IsCancellationRequested)

{

Console.Write("*");

Thread.Sleep(1000);

}

}, token).ContinueWith((t) =>

{

t.Exception.Handle((e) => true);

Console.WriteLine("You have canceled the task");

}, TaskContinuationOptions.OnlyOnCanceled);

If you want to cancel a Task after a certain amount of time, you can use an overload of Task.WaitAny that takes a timeout. Example 1-45 shows an example.

Example 1-45. Setting a timeout on a task

Task longRunning = Task.Run(() =>

{

Thread.Sleep(10000);

});

int index = Task.WaitAny(new[] { longRunning }, 1000);

if (index == -1)

Console.WriteLine("Task timed out");

If the returned index is -1, the task timed out. It’s important to check for any possible errors on the other tasks. If you don’t catch them, they will go unhandled.

THOUGHT EXPERIMENT

Implementing multithreading

In this thought experiment, apply what you’ve learned about this objective. You can find answers to these questions in the Answers section at the end of this chapter.

You are experiencing deadlocks in your code. It’s true that you have a lot of locking statements and you are trying to improve your code to avoid the deadlocks.

1. How can you orchestrate your locking code to avoid deadlocks?

2. How can the Interlocked class help you?

Objective summary

§ When accessing shared data in a multithreaded environment, you need to synchronize access to avoid errors or corrupted data.

§ Use the lock statement on a private object to synchronize access to a piece of code.

§ You can use the Interlocked class to execute simple atomic operations.

§ You can cancel tasks by using the CancellationTokenSource class with a CancellationToken.

Objective review

Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the Answers section at the end of this chapter.

1. You want to synchronize access by using a lock statement. On which member do you lock?

a. this

b. string _lock = “mylock”

c. int _lock = 42;

d. object _lock = new object();

2. You need to implement cancellation for a long running task. Which object do you pass to the task?

a. CancellationTokenSource

b. CancellationToken

c. Boolean isCancelled variable

d. Volatile

3. You are implementing a state machine in a multithreaded class. You need to check what the current state is and change it to the new one on each step. Which method do you use?

a. Volatile.Write(ref currentState)

b. Interlocked.CompareExchange(ref currentState, ref newState, expectedState)

c. Interlocked.Exchange(ref currentState, newState)

d. Interlocked.Decrement(ref newState)

Objective 1.3: Implement program flow

One important aspect of managing the flow of your program is making decisions in your application, including checking to see whether the user has entered the correct password, making sure that a certain value is within range, or one of the myriad other possibilities. C# offers a couple of statements that can be used when you need to make a decision.

Next to making decisions, another common task is working with collections. C# has language features that help you work with collections by allowing you to iterate over collections and access individual items.

THIS OBJECTIVE COVERS HOW TO:

§ Work with Boolean expressions.

§ Make decisions in your application.

§ Iterate across collections.

§ Use explicit jump statements.

Working with Boolean expressions

When working with flow control statements, you will automatically work with Boolean expressions. A Boolean expression should always produce true or false as the end result, but in doing so they can be quite complex by using different operators.

One such an operator is the equality operator (==). You use this one to test that two values are equal to each other. Example 1-46 shows some examples.

Example 1-46. A better readable nested if statement

int x = 42;

int y = 1;

int z = 42;

Console.WriteLine(x == y); // Displays false

Console.WriteLine(x == z); // Displays true

Table 1-1 shows the operators that you can use in C#.

Table 1-1. C# relational and equality operators

Operator

Description

Example

<

Less than

x < 42;

>

Greater than

x > 42;

<=

Less than or equal to

x <= 42;

>=

Greater than or equal to

x >= 42;

==

Equal to

x == 42;

!=

Not equal to

x != 42;

You can combine these operators by using the OR (||), AND (&&), and Exclusive OR (^) operators. These operators use both a left and a right operand, meaning the left and right part of the expression.

The OR operator returns true when one of both operands is true. If both are false, it returns false. If both are true, it will return true. Example 1-47 shows an example.

Example 1-47. Boolean OR operator

bool x = true;

bool y = false;

bool result = x || y;

Console.WriteLine(result); // Displays True

If the runtime notices that the left part of your OR operation is true, it doesn’t have to evaluate the right part of your expression. This is called short-circuiting. Example 1-48 shows an example.

Example 1-48. Short-circuiting the OR operator

public void OrShortCircuit()

{

bool x = true;

bool result = x || GetY();

}

private bool GetY()

{

Console.WriteLine("This method doesn't get called");

return true;

}

In this case, the method GetY is never called and the line is not written to the console.

The AND operator can be used when both parts of an expression need to be true. If either one of the operands is false, the whole expression evaluates to false. Example 1-49 uses the AND operator to check to see whether a value is within a certain range.

Example 1-49. Using the AND operator

int value = 42;

bool result = (0 < value) && (value < 100)

In this case, it’s not required to add the extra parentheses around the left and right operand but it does add to the readability of your code. Just as with the OR operator, the runtime applies short-circuiting. Next to being a performance optimization, you can also use it to your advantage when working with null values. Example 1-50 for example uses the AND operator to check if the input argument is not null and to execute a method on it. If short-circuiting wouldn’t be used in this situation, the code would throw an exception each time the input parameter would be null.

Example 1-50. Using the AND operator

public void Process(string input)

{

bool result = (input != null) && (input.StartsWith("v"));

// Do something with the result

}

The Exclusive OR operator (XOR) returns true only when exactly one of the operands is true. Table 1-2 gives the possibilities for the XOR operator.

Table 1-2. Possible values for the XOR operator

Left operand

Right operand

Result

True

True

False

True

False

True

False

True

True

False

False

False

Because the XOR operator has to check that exactly one of the operands is true, it doesn’t apply short-circuiting. Example 1-51 shows how to use the XOR operator.

Example 1-51. Using the XOR operator

bool a = true;

bool b = false;

Console.WriteLine(a ^ a); // False

Console.WriteLine(a ^ b); // True

Console.WriteLine(b ^ b); // False

Making decisions

C# offers several flow control statements that help you determine the path that your application follows. You can use the following statements:

§ if

§ while

§ do while

§ for

§ foreach

§ switch

§ break

§ continue

§ goto

§ Null-coalescing operator (??)

§ Conditional operator (?:)

Using these constructs, you can create flexible applications that enable you to execute different behavior depending on the circumstances. It’s important to know these statements and be able to choose between them.

The if statement

The most widely used flow control statement is the if statement. The if statement enables you to execute a piece of code depending on a specific condition. The general syntax for the if statement is this:

if (boolean-expression)

statement to execute

The statement to execute is executed only if the boolean expression evaluates to true. Example 1-52 shows an example of using if.

Example 1-52. Basic if statement

bool b = true;

if (b)

Console.WriteLine("True");

In this case, the application outputs “True” because the condition for the if statement is true. If b would be false, the Console.WriteLine statement would not be executed.

Of course, passing a hard-coded value to the if statement is not very useful. Normally, you would use the if statement with a more dynamic value that can change during the execution of the application.

When working with program flow statements, it’s important to know the concept of a code block, which enables you to write multiple statements in a context in which only a single statement is allowed.

A block uses curly-braces to denote its start and end:

{

statements

}

Example 1-52 showed an if statement that executes a single line of code only if it’s true. You can, however, also use a code block after the if statement. All code in the block is executed based on the result of the if statement. You can see an example of this in Example 1-53.

Example 1-53. An if statement with code block

bool b = true;

if (b)

{

Console.WriteLine("Both these lines");

Console.WriteLine("Will be executed");

}

Variables defined within a code block are accessible only within the code block and go out of scope at the end of the block. This means that you can declare variables inside a block, and use them within the block but not outside the block. Example 1-54 shows the scoping differences. Variableb is declared outside the block and can be accessed both in the outer block and in the if statement. Variable r, however, can be accessed only in the if statement.

Example 1-54. Code blocks and scoping

bool b = true;

if (b)

{

int r = 42;

b = false;

}

// r is not accessible

// b is now false

You can also execute some code when the if statement evaluates to false. You can do this by using an else block. The general syntax looks like this:

if (boolean-expression)

statement

else

statement

Example 1-55 shows an example of using an else statement. This outputs “False”.

Example 1-55. Using an else statement

bool b = false;

if (b)

{

Console.WriteLine("True");

}

else

{

Console.WriteLine("False");

}

You can use multiple if/else statements as shown in Example 1-56.

Example 1-56. Using multiple if/else statements

bool b = false;

bool c = true;

if (b)

{

Console.WriteLine("b is true");

}

else if (c)

{

Console.WriteLine("c is true");

}

else

{

Console.WriteLine("b and c are false");

}

You can also nest if and else statements. For readability, it’s nice to outline your code correctly. The following code is perfectly legal, but on first sight it’s hard to see what the code really does:

if (x) if (y) F(); else G();

When outlined correctly, the code is equal to the one in Example 1-57, which is much easier to understand.

Example 1-57. A more readable nested if statement

if (x)

{

if (y)

{

F();

}

else

{

G();

}

}

The compiler optimizes your code and removes any unnecessary braces and statements. Under normal circumstances, you should worry more about readability than about the number of lines you produce. Team members especially appreciate it when you write code that’s not only correct but also easier to maintain.

The null-coalescing operator

The ?? operator is called the null-coalescing operator. You can use it to provide a default value for nullable value types or for reference types.

The operator returns the left value if it’s not null; otherwise, the right operand.

Example 1-58 shows an example of using the operator.

Example 1-58. The null-coalescing operator

int? x = null;

int y = x ?? -1;

In this case, the value of y is -1 because x is null.

You can also nest the null-coalescing operator, as Example 1-59 shows.

Example 1-59. Nesting the null-coalescing operator

int? x = null;

int? z = null;

int y = x ??

z ??

-1;

Of course, you can achieve the same with an if statement but the null-coalescing operator can shorten your code and improve its readability.

The conditional operator

The conditional operator (?:) returns one of two values depending on a Boolean expression. If the expression is true, the first value is returned; otherwise, the second.

Example 1-60 shows an example of how the operator can be used to simplify some code. In this case, the if statement can be replaced with the conditional operator.

Example 1-60. The conditional operator

private static int GetValue(bool p)

{

if (p)

return 1;

else

return 0;

return p ? 1 : 0;

}

The switch statement

You can use the switch statement to simplify complex if statements. Take the example of Example 1-61.

Example 1-61. A complex if statement

void Check(char input)

{

if (input == 'a'

|| input == 'e'

|| input == 'i'

|| input == 'o'

|| input == 'u')

{

Console.WriteLine("Input is a vowel");

}

else

{

Console.WriteLine("Input is a consonant");

}

}

The switch statement can be used to make this code more comprehensive. A switch statement checks the value of its argument and then looks for a matching label. Example 1-62 shows the code from Example 1-59 as a switch statement.

Example 1-62. A switch statement

void CheckWithSwitch(char input)

{

switch (input)

{

case 'a':

case 'e':

case 'i':

case 'o':

case 'u':

{

Console.WriteLine("Input is a vowel");

break;

}

case 'y':

{

Console.WriteLine("Input is sometimes a vowel.");

break;

}

default:

{

Console.WriteLine("Input is a consonant");

break;

}

}

}

A switch can use one or multiple switch-sections that can contain one or more switchlabels. In Example 1-62, all the vowels belong to the same switch-section. If you want, you can also add a default label that is used when none of the other labels matches.

The end point of a switch statement should not be reachable. You need to have a statement such as break or return that explicitly exits the switch statement, or you need to throw an exception. This avoids the fall-through behavior that C++ has. This makes it possible for switch sections to appear in any order without affecting behavior.

Instead of implicitly falling through to another label, you can use the goto statement (see Example 1-63).

Example 1-63. goto in a switch statement

int i = 1;

switch (i)

{

case 1:

{

Console.WriteLine("Case 1");

goto case 2;

}

case 2:

{

Console.WriteLine("Case 2");

break;

}

}

// Displays

// Case 1

// Case 2

Iterating across collections

Another subject that has to do with the flow of your program is iterating across collections. Collections are widely used in C#, and the language offers constructs that you can use with them:

§ for

§ foreach

§ while

§ do while

The for loop

You can use a for loop when you need to iterate over a collection until a specific condition is reached (for example, you have reached the end of a collection). Example 1-64 shows an example in which you loop through all items in an array.

Example 1-64. A basic for loop

int[] values = { 1, 2, 3, 4, 5, 6 };

for (int index = 0; index < values.Length; index++)

{

Console.Write(values[index]);

}

// Displays

// 123456

As you can see, the for loop consists of three different parts:

for(initial; condition; loop)

§ The initial part is executed before the first iteration and declares and initializes the variables that are used in the loop.

§ The condition is evaluated on each iteration. When the condition equals false, the loop is exited.

§ The loop section is run during every iteration and is normally used to change the counter that’s used to loop over the collection.

None of these parts is required. You can use for(;;) {} as a perfectly legal for loop that would never end. You can also use multiple statements in each part of your for loop (see Example 1-65).

Example 1-65. A for loop with multiple loop variables

int[] values = { 1, 2, 3, 4, 5, 6 };

for (int x = 0, y = values.Length - 1;

((x < values.Length) && (y >= 0));

x++, y--)

{

Console.Write(values[x]);

Console.Write(values[y]);

}

// Displays

// 162534435261

It’s also not required to let the loop value increment or decrement with 1. For example, you can change Example 1-64 to increment index with 2 to only display the odd numbers, as Example 1-66 shows.

Example 1-66. A for loop with a custom increment

int[] values = { 1, 2, 3, 4, 5, 6 };

for (int index = 0; index < values.Length; index += 2)

{

Console.Write(values[index]);

}

// Displays

// 135

Normally, the for loop ends when the condition becomes false, but you can also decide to manually break out of the loop. You can do this by using the break or return statement when you want to completely exit the method. Example 1-67 shows an example of the break statement.

Example 1-67. A for loop with a break statement

int[] values = { 1, 2, 3, 4, 5, 6 };

for (int index = 0; index < values.Length; index++)

{

if (values[index] == 4) break;

Console.Write(values[index]);

}

// Displays

// 123

Next to breaking the loop completely, you can also instruct the for loop to continue to the next item by using the continue statement. Example 1-68 shows an example in which the number 4 is skipped in the loop.

Example 1-68. A for loop with a continue statement

int[] values = { 1, 2, 3, 4, 5, 6 };

for (int index = 0; index < values.Length; index++)

{

if (values[index] == 4) continue;

Console.Write(values[index]);

}

// Displays

// 12356

The while and do-while loop

Another looping construction is the while loop. A for loop is nothing more than a convenient way to write a while loop that does the checking and incrementing of the counter. Example 1-69 shows an example. Notice the extra parenthesis to restrict the scope of the loop variable.

Example 1-69. A for loop with a continue statement.

int[] values = { 1, 2, 3, 4, 5, 6 };

{

int index = 0;

while (index < values.Length)

{

Console.Write(values[index]);

index++;

}

}

As you can see, a while loop checks an expression and executes as long as this expression is true. You should use a for loop when you know the number of iterations in advance. A while loop can be used when you don’t know the number of iterations.

If the condition of the while loop is false, it won’t execute the code inside the loop. This is different when using a do-while loop. A do-while loop executes at least once, even if the expression is false. Example 1-70 shows an example of using a do-while loop.

Example 1-70. do-while loop

do

{

Console.WriteLine("Executed once!");

}

while (false);

Within a while or do-while loop, you can use the continue and break statements just as with a for loop.

The foreach loop

The foreach loop is used to iterate over a collection and automatically stores the current item in a loop variable. The foreach loop keeps track of where it is in the collection and protects you against iterating past the end of the collection.

Example 1-71 shows an example of how to use the foreach loop.

Example 1-71. foreach loop

int[] values = { 1, 2, 3, 4, 5, 6 };

foreach (int i in values)

{

Console.Write(i);

}

// Displays 123456

As you can see, the foreach loop automatically stores the current item in a strongly typed variable. You can use the continue and break statements to influence the way the foreach loop works.

The loop variable cannot be modified. You can make modifications to the object that the variable points to, but you can’t assign a new value to it. Example 1-72 shows these differences.

Example 1-72. Changing items in a foreach

class Person

{

public string FirstName { get; set; }

public string LastName { get; set; }

}

void CannotChangeForeachIterationVariable()

{

var people = new List<Person>

{

new Person() { FirstName = "John", LastName = "Doe"},

new Person() { FirstName = "Jane", LastName = "Doe"},

};

foreach (Person p in people)

{

p.LastName = "Changed"; // This is allowed

// p = new Person(); // This gives a compile error

}

}

You can understand this behavior when you know how foreach actually works. When the compiler encounters a foreach statement, it generates some code on your behalf; foreach is syntactic sugar that lets you write some code in a nice way. Example 1-73 shows what’s happening.

Example 1-73. The compiler-generated code for a foreach loop

List<Person>.Enumerator e = people.GetEnumerator();

try

{

Person v;

while (e.MoveNext())

{

v = e.Current;

}

}

finally

{

System.IDisposable d = e as System.IDisposable;

if (d != null) d.Dispose();

}

If you change the value of e.Current to something else, the iterator pattern can’t determine what to do when e.MoveNext is called. This is why it’s not allowed to change the value of the iteration variable in a foreach statement.

MORE INFO: ENUMERATORS

For more information on how the GetEnumerator method works and how you can implement your own enumerators see Chapter 2.

Jump statements

Another type of statement that can be used to influence program flow is a jump statement. You have already looked at two of those statements: break and continue. A jump statement unconditionally transfers control to another location in your code.

Another jump statement that can be used to change the flow of a program is goto. The goto statement moves control to a statement that is marked by a label. If the label can’t be found or is not within the scope of the goto statement, a compiler error occurs.

Example 1-74 shows an example of using goto and a label.

Example 1-74. goto statement with a label

int x = 3;

if ( x == 3) goto customLabel;

x++;

customLabel:

Console.WriteLine(x);

// Displays 3

You cannot make a jump to a label that’s not in scope. This means you cannot transfer control to another block of code that’s outside of your current block. The compiler also makes sure that any finally blocks that intervene are executed.

The jump statements such as break and continue can have their uses in some situations. If possible, however, you should try to avoid them. By refactoring your code, you can remove them most of the time and this will improve the readability of your code.

The goto statement is even worse. It is considered a bad practice. Although C# restricts the way the goto operator behaves, as a guideline, you should try to avoid using goto. One area where goto is used is in generated code like the code the compiler generates when you use the newasync/await feature in C# 5.

MORE INFO: JUMP STATEMENTS

For more information about jump statements, see http://msdn.microsoft.com/en-us/library/d96yfwee.aspx.

THOUGHT EXPERIMENT

Choosing your program flow statements

In this thought experiment, apply what you’ve learned about this objective. You can find answers to these questions in the Answers section at the end of this chapter.

You are updating an old C#2 console application to a WPF C#5 application. The application is used by hotels to keep track of reservations and guests coming and leaving. You are going through the old code base to determine whether there is code that can be easily reused. You notice a couple of things:

§ The code uses the goto statement to manage flow.

§ There are a lot of long if statements that map user input.

§ The code uses the for loop extensively.

1. What is the disadvantage of using goto? How can you avoid using the goto statement?

2. Which statement can you use to improve the long if statements?

3. What are the differences between the for and foreach statement? When should you use which?

Objective summary

§ Boolean expressions can use several operators: ==, !=, <, >, <=, >=, !. Those operators can be combined together by using AND (&&), OR (||) and XOR (^).

§ You can use the if-else statement to execute code depending on a specific condition.

§ The switch statement can be used when matching a value against a couple of options.

§ The for loop can be used when iterating over a collection where you know the number of iterations in advance.

§ A while loop can be used to execute some code while a condition is true; do-while should be used when the code should be executed at least once.

§ foreach can be used to iterate over collections.

§ Jump statements such as break, goto, and continue can be used to transfer control to another line of the program.

Objective review

Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the Answers section at the end of this chapter.

1. You need to iterate over a collection in which you know the number of items. You need to remove certain items from the collection. Which statement do you use?

a. switch

b. foreach

c. for

d. goto

2. You have a lot of checks in your application for null values. If a value is not null, you want to call a method on it. You want to simplify your code. Which technique do you use?

a. for

b. Conditional operator

c. Null-coalescing operator

d. The short-circuiting behavior of the and operator

3. You are processing some data from over the network. You use a HasNext and Read method to retrieve the data. You need to run some code on each item. What do you use?

a. for

b. foreach

c. while

d. do-while

Objective 1.4: Create and implement events and callbacks

An event can be used to provide notifications. You can subscribe to an event if you are interested in those notifications. You can also create your own events and raise them to provide notifications when something interesting happens. The .NET Framework offers built-in types that you can use to create events. By using delegates, lambda expressions, and anonymous methods, you can create and use events in a comfortable way.

THIS OBJECTIVE COVERS HOW TO:

§ Understand delegates.

§ Use lambda expressions.

§ Create and raise events.

Understanding delegates

In C#, delegates form the basic building blocks for events. A delegate is a type that defines a method signature. In C++, for example, you would do this with a function pointer. In C# you can instantiate a delegate and let it point to another method. You can invoke the method through thedelegate.

Example 1-75 shows an example of declaring a delegate and calling a method through it.

Example 1-75. Using a delegate

public delegate int Calculate(int x, int y);

public int Add(int x, int y) { return x + y; }

public int Multiply(int x, int y) { return x * y; }

public void UseDelegate()

{

Calculate calc = Add;

Console.WriteLine(calc(3, 4)); // Displays 7

calc = Multiply;

Console.WriteLine(calc(3, 4)); // Displays 12

}

As you can see, you use the special delegate keyword to tell the compiler that you are creating a delegate type. Delegates can be nested in other types and they can then be used as a nested type.

Instantiating delegates is easy since C# 2.0 added the automatic creation of a new delegate when a method group is assigned to a delegate type. An instantiated delegate is an object; you can pass it around and give it as an argument to other methods.

Another feature of delegates is that you can combine them together. This is called multicasting. You can use the + or += operator to add another method to the invocation list of an existing delegate instance. Example 1-76 shows an example.

Example 1-76. A multicast delegate

public void MethodOne()

{

Console.WriteLine("MethodOne");

}

public void MethodTwo()

{

Console.WriteLine("MethodTwo");

}

public delegate void Del();

public void Multicast()

{

Del d = MethodOne;

d += MethodTwo;

d();

}

// Displays

// MethodOne

// MethodTwo

You can also remove a method from an invocation list by using the decrement assignment operator (- or -=).

All this is possible because delegates inherit from the System.MulticastDelegate class that in turn inherits from System.Delegate. Because of this, you can use the members that are defined in those base classes on your delegates.

For example, to find out how many methods a multicast delegate is going to call, you can use the following code:

int invocationCount = del.GetInvocationList().GetLength(0);

When you assign a method to a delegate, the method signature does not have to match the delegate exactly. This is called covariance and contravariance. Covariance makes it possible that a method has a return type that is more derived than that defined in the delegate. Contravariance permits a method that has parameter types that are less derived than those in the delegate type.

Example 1-77 shows an example of covariance.

Example 1-77. Covariance with delegates

public delegate TextWriter CovarianceDel();

public StreamWriter MethodStream() { return null; }

public StringWriter MethodString() { return null; }

CovarianceDel del;

del = MethodStream;

del = MethodString;

Because both StreamWriter and StringWriter inherit from TextWriter, you can use the CovarianceDel with both methods. An example of contravariance can be seen in Example 1-78.

Example 1-78. Contravariance with delegates

void DoSomething(TextWriter tw) { }

public delegate void ContravarianceDel(StreamWriter tw);

ContravarianceDel del = DoSomething;

Because the method DoSomething can work with a TextWriter, it surely can also work with a StreamWriter. Because of contravariance, you can call the delegate and pass an instance of StreamWriter to the DoSomething method.

MORE INFO: COVARIANCE AND CONTRAVARIANCE

For more information on covariance and contravariance and how they are implemented in C#, see the excellent series of blog posts that Eric Lippert wrote at http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/.

Using lambda expressions

Sometimes the whole signature of a method can be more code than the body of a method. There are also situations in which you need to create an entire method only to use it in a delegate.

For these cases, Microsoft added some new features to C#. In C#, 2.0 anonymous methods were added. In C# 3.0, things became even better when lambda expressions were added. Lambda expressions are the preferred way to go when writing new code.

Example 1-79 shows how you would write the example in Example 1-73 with newer lambda syntax.

Example 1-79. Lambda expression to create a delegate

Calculate calc = (x, y) => x + y;

Console.WriteLine(calc(3, 4)); // Displays 7

calc = (x, y) => x * y;

Console.WriteLine(calc(3, 4)); // Displays 12

When reading this code, you can say go or goes to for the special lambda syntax. For example, the first lambda expression in Example 1-79 is read as “x and y goes to adding x and y.”

The lambda function has no specific name as the methods in Example 1-75 have. Because of this, lambda functions are called anonymous functions. You also don’t have to specify a return type explicitly. The compiler infers this automatically from your lambda. And in the case of Example 1-79, the types of the parameters x and y are also not specified explicitly.

As you can see, the syntax for writing a lambda can be compact. If a lambda has only one parameter, you can even remove the parentheses around the parameter.

You can create lambdas that span multiple statements. You can do this by adding curly braces around the statements that form the lambda as Example 1-80 shows.

Example 1-80. Creating a lambda expression with multiple statements

Calculate calc =

(x, y) =>

{

Console.WriteLine("Adding numbers");

return x + y;

};

int result = calc(3, 4);

// Displays

// Adding numbers

Sometimes declaring a delegate for an event feels a bit cumbersome. Because of this, the .NET Framework has a couple of built-in delegate types that you can use when declaring delegates. For the Calculate examples, you have used the following delegate:

public delegate int Calculate(int x, int y);

You can replace this delegate with one of the built-in types namely Func<int,int,int>. The Func<...> types can be found in the System namespace and they represent delegates that return a type and take 0 to 16 parameters. All those types inherit from System.MulticastDelegate so you can add multiple methods to the invocation list.

If you want a delegate type that doesn’t return a value, you can use the System.Action types. They can also take 0 to 16 parameters, but they don’t return a value. Example 1-81 shows an example of using the Action type.

Example 1-81. Using the Action delegate

Action<int, int> calc = (x, y) =>

{

Console.WriteLine(x + y);

};

calc(3, 4); // Displays 7

Things start to become more complex when your lambda function starts referring to variables declared outside of the lambda expression (or to the this reference). Normally, when control leaves the scope of a variable, the variable is no longer valid. But what if a delegate refers to a localvariable and is then returned to the calling method? Now, the delegate has a longer life than the variable. To fix this, the compiler generates code that makes the life of the captured variable at least as long as the longest-living delegate. This is called a closure.

Using events

A popular design pattern (a reusable solution for a recurring problem) in application development is that of publish-subscribe. You can subscribe to an event and then you are notified when the publisher of the event raises a new event. This is used to establish loose coupling between components in an application.

Delegates form the basis for the event system in C#. Example 1-82 shows how a class can expose a public delegate and raise it.

Example 1-82. Using an Action to expose an event

public class Pub

{

public Action OnChange { get; set; }

public void Raise()

{

if (OnChange != null)

{

OnChange();

}

}

}

public void CreateAndRaise()

{

Pub p = new Pub();

p.OnChange += () => Console.WriteLine("Event raised to method 1");

p.OnChange += () => Console.WriteLine("Event raised to method 2");

p.Raise();

}

When calling CreateAndRaise, your code creates a new instance of Pub, subscribes to the event with two different methods and then raises the event by calling p.Raise. The Pub class is completely unaware of any subscribers. It just raises the event.

If there would be no subscribers to an event, the OnChange property would be null. This is why the Raise method checks to see whether OnChange is not null.

Although this system works, there are a couple of weaknesses. If you change the subscribe line for method 2 to the following, you would effectively remove the first subscriber by using = instead of +=:

p.OnChange = () => Console.WriteLine("Event raised to method 2");

In the example from Example 1-82, the Pub class raises the event. However, nothing prevents outside users of the class from raising the event. By just calling p.OnChange() every user of the class can raise the event to all subscribers.

To overcome these weaknesses, the C# language uses the special event keyword. Example 1-83 shows a modified example of the Pub class that uses the event syntax.

Example 1-83. Using the event keyword

public class Pub

{

public event Action OnChange = delegate { };

public void Raise()

{

OnChange();

}

}

By using the event syntax, there are a couple of interesting changes. First, you are no longer using a public property but a public field. Normally, this would be a step back. However, with the event syntax, the compiler protects your field from unwanted access.

An event cannot be directly assigned to (with the = instead of +=) operator. So you don’t have the risk of someone removing all previous subscriptions, as with the delegate syntax.

Another change is that no outside users can raise your event. It can be raised only by code that’s part of the class that defined the event.

Example 1-83 also uses some special syntax to initialize the event to an empty delegate. This way, you can remove the null check around raising the event because you can be certain that the event is never null. Outside users of your class can’t set the event to null; only members of your class can. As long as none of your other class members sets the event to null, you can safely assume that it will always have a value.

There is, however, one change you still have to make to follow the coding conventions in the .NET Framework. Instead of using the Action type for your event, you should use the EventHandler or EventHandler<T>. EventHandler is declared as the following delegate:

public delegate void EventHandler(object sender, EventArgs e);

By default, it takes a sender object and some event arguments. The sender is by convention the object that raised the event (or null if it comes from a static method). By using EventHandler<T>, you can specify the type of event arguments you want to use. Example 1-84 shows an example.

Example 1-84. Custom event arguments

public class MyArgs : EventArgs

{

public MyArgs(int value)

{

Value = value;

}

public int Value { get; set; }

}

public class Pub

{

public event EventHandler<MyArgs> OnChange = delegate { };

public void Raise()

{

OnChange(this, new MyArgs(42));

}

}

public void CreateAndRaise()

{

Pub p = new Pub();

p.OnChange += (sender, e)

=> Console.WriteLine("Event raised: {0}", e.Value);

p.Raise();

}

The Pub class uses an EventHandler<MyArgs>, which specifies the type of the event arguments. When raising this event, you are required to pass an instance of MyArgs. Subscribers to the event can access the arguments and use it.

Although the event implementation uses a public field, you can still customize addition and removal of subscribers. This is called a custom event accessor. Example 1-85 shows an example of creating a custom event accessor for an event.

Example 1-85. Custom event accessor

public class Pub

{

private event EventHandler<MyArgs> onChange = delegate { };

public event EventHandler<MyArgs> OnChange

{

add

{

lock (onChange)

{

onChange += value;

}

}

remove

{

lock (onChange)

{

onChange -= value;

}

}

}

public void Raise()

{

onChange(this, new MyArgs(42));

}

}

A custom event accessor looks a lot like a property with a get and set accessor. Instead of get and set you use add and remove. It’s important to put a lock around adding and removing subscribers to make sure that the operation is thread safe.

MORE INFO: USING LOCK

For more info on when and how to use locking, see the section titled “Objective 1.2: Manage multithreading,” earlier in this chapter.

If you use the regular event syntax, the compiler generates the accessor for you. This makes it clear that events are not delegates; instead they are a convenient wrapper around delegates.

Delegates are executed in a sequential order. Generally, delegates are executed in the order in which they were added, although this is not something that is specified within the Common Language Infrastructure (CLI), so you shouldn’t depend on it.

One thing that is a direct result of the sequential order is how to handle exceptions. Example 1-86 shows an example in which one of the event subscribers throws an error.

Example 1-86. Exception when raising an event

public class Pub

{

public event EventHandler OnChange = delegate { };

public void Raise()

{

OnChange(this, EventArgs.Empty);

}

}

public void CreateAndRaise()

{

Pub p = new Pub();

p.OnChange += (sender,e )

=> Console.WriteLine("Subscriber 1 called");

p.OnChange += (sender, e)

=> { throw new Exception(); };

p.OnChange += (sender,e )

=> Console.WriteLine("Subscriber 3 called");

p.Raise();

}

// Displays

// Subscriber 1 called

As you can see, the first subscriber is executed successfully, the second one throws an exception, and the third one is never called.

If this is not the behavior you want, you need to manually raise the events and handle any exceptions that occur. You can do this by using the GetInvocationList method that is declared on the System.Delegate base class. Example 1-87 shows an example of retrieving the subscribers and enumerating them manually.

Example 1-87. Manually raising events with exception handling

public class Pub

{

public event EventHandler OnChange = delegate { };

public void Raise()

{

var exceptions = new List<Exception>();

foreach (Delegate handler in OnChange.GetInvocationList())

{

try

{

handler.DynamicInvoke(this, EventArgs.Empty);

}

catch (Exception ex)

{

exceptions.Add(ex);

}

}

if (exceptions.Any())

{

throw new AggregateException(exceptions);

}

}

}

public void CreateAndRaise()

{

Pub p = new Pub();

p.OnChange += (sender, e)

=> Console.WriteLine("Subscriber 1 called");

p.OnChange += (sender, e)

=> { throw new Exception(); };

p.OnChange += (sender, e)

=> Console.WriteLine("Subscriber 3 called");

try

{

p.Raise();

}

catch (AggregateException ex)

{

Console.WriteLine(ex.InnerExceptions.Count);

}

}

// Displays

// Subscriber 1 called

// Subscriber 3 called

// 1

MORE INFO: EXCEPTION HANDLING

For more information on how to work with exceptions, see the section titled Objective 1.5: Implement exception handling later in this chapter.

THOUGHT EXPERIMENT

Building a loosely coupled system

In this thought experiment, apply what you’ve learned about this objective. You can find answers to these questions in the Answers section at the end of this chapter.

You are working on a desktop application that consists of multiple forms. Those forms show different views of the same data and they should update in real time. Your application is extensible, and third parties can add plug-ins that contain their own views of the data.

1. Should you use delegates or events in this system?

2. How can this help you?

Objective summary

§ Delegates are a type that defines a method signature and can contain a reference to a method.

§ Delegates can be instantiated, passed around, and invoked.

§ Lambda expressions, also known as anonymous methods, use the => operator and form a compact way of creating inline methods.

§ Events are a layer of syntactic sugar on top of delegates to easily implement the publish-subscribe pattern.

§ Events can be raised only from the declaring class. Users of events can only remove and add methods the invocation list.

§ You can customize events by adding a custom event accessor and by directly using the underlying delegate type.

Objective review

Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the Answers section at the end of this chapter.

1. You have a private method in your class and you want to make invocation of the method possible by certain callers. What do you do?

a. Make the method public.

b. Use an event so outside users can be notified when the method is executed.

c. Use a method that returns a delegate to authorized callers.

d. Declare the private method as a lambda.

2. You have declared an event on your class, and you want outside users of your class to raise this event. What do you do?

a. Make the event public.

b. Add a public method to your class that raises the event.

c. Use a public delegate instead of an event.

d. Use a custom event accessor to give access to outside users.

3. You are using a multicast delegate with multiple subscribers. You want to make sure that all subscribers are notified, even if an exception is thrown. What do you do?

a. Manually raise the events by using GetInvocationList.

b. Wrap the raising of the event in a try/catch.

c. Nothing. This is the default behavior.

d. Let subscribers return true or false instead of throwing an exception.

Objective 1.5: Implement exception handling

When you build your applications, sometimes errors occur. Maybe you want to write a file to disk, and the disk is full. You try to connect to a database, but the database server is unavailable or another unexpected condition exists. Instead of working with error codes, the .NET Framework uses exceptions to signal errors. You can also use these exceptions to signal errors that happen in your own applications and you can even create custom exception types to signal specific errors.

It’s important to know how to work with exceptions so you can implement a well-designed strategy for dealing with or raising errors.

THIS OBJECTIVE COVERS HOW TO:

§ Handle exceptions.

§ Throw exceptions.

§ Create custom exceptions.

Handling exceptions

When an error occurs somewhere in an application, an exception is raised. Exceptions have a couple of advantages compared with error codes. An exception is an object in itself that contains data about the error that happened. It not only has a user-friendly message but it also contains the location in which the error happened and it can even store extra data, such as an address to a page that offers some help.

If an exception goes unhandled, it will cause the current process to terminate. Example 1-88 shows an example of an application that throws an error and shuts down.

Example 1-88. Parsing an invalid number

namespace ExceptionHandling

{

public static class Program

{

public static void Main()

{

string s = "NaN";

int i = int.Parse(s);

}

}

}

// Displays

// Unhandled Exception: System.FormatException: Input string was not in a correct format.

// at System.Number.StringToNumber(String str, NumberStyles options,

// NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)

// at System.Number.ParseInt32(String s, NumberStyles style,

// NumberFormatInfo info)

// at System.Int32.Parse(String s)

// at ExceptionHandling.Program.Main() in c:\Users\Wouter\Documents\

// Visual Studio 2012\Projects\ExamRefProgrammingInCSharp\Chapter1\Program.cs:line 9

The int.Parse method throws an exception of type FormatException when the string is not a valid number. Throwing an exception halts the execution of your application. Instead of continuing to the following line, the runtime starts searching for a location in which you handle the exception. If such a location cannot be found, the exception is unhandled and will terminate the application.

To handle an exception, you can use a try/catch statement. Example 1-89 shows an example of catching the FormatException.

Example 1-89. Catching a FormatException

using System;

namespace ExceptionHandling

{

public static class Program

{

public static void Main()

{

while (true)

{

string s = Console.ReadLine();

if (string.IsNullOrWhiteSpace(s)) break;

try

{

int i = int.Parse(s);

break;

}

catch (FormatException)

{

Console.WriteLine("{0} is not a valid number. Please try again", s);

}

}

}

}

}

You need to surround the code that can potentially throw an exception with a try statement. Following the try statement, you can add several different catch blocks. How much code you put inside each try block depends on the situation. If you have multiple statements that can throw the same exceptions that need to be handled differently, they should be in different try blocks.

A catch block can specify the type of the exception it wants to catch. Because all exceptions in the .NET Framework inherit from System.Exception, you can catch every possible exception by catching this base type. You can catch more specific exception types by adding extra catch blocks.

The catch blocks should be specified as most-specific to least-specific because this is the order in which the runtime will examine them. When an exception is thrown, the first matching catch block will be executed. If no matching catch block can be found, the exception will fall through. Example 1-90 shows an example of catching two different exception types.

If the string s is null, an ArgumentNullException will be thrown. If the string is not a number, a FormatException will be thrown. By using different catch blocks, you can handle those exceptions each in their own way.

Example 1-90. Catching different exception types

try

{

int i = int.Parse(s);

}

catch (ArgumentNullException)

{

Console.WriteLine("You need to enter a value");

}

catch (FormatException)

{

Console.WriteLine("{0} is not a valid number. Please try again", s);

}

In C# 1, you could also use a catch block without an exception type. This could be used to catch exceptions that were thrown from other languages like C++ that don’t inherit from System.Exception (in C++ you can throw exceptions of any type). Nowadays, each exception that doesn’t inherit from System.Exception is automatically wrapped in a System.Runtime.CompilerServices.RuntimeWrappedException. Since this exception inherits from System.Exception, there is no need for the empty catch block anymore.

It’s important to make sure that your application is in the correct state when the catch block finishes. This could mean that you need to revert changes that your try block made before the exception was thrown.

Another important feature of exception handling is the ability to specify that certain code should always run in case of an exception. This can be done by using the finally block together with a try or try/catch statement. The finally block will execute whether an exception happens or not. Example 1-91 shows an example of a finally block.

Example 1-91. Using a finally block

using System;

namespace ExceptionHandling

{

public static class Program

{

public static void Main()

{

string s = Console.ReadLine();

try

{

int i = int.Parse(s);

}

catch (ArgumentNullException)

{

Console.WriteLine("You need to enter a value");

}

catch (FormatException)

{

Console.WriteLine("{0} is not a valid number. Please try again", s);

}

finally

{

Console.WriteLine("Program complete.");

}

}

}

}

// Displays

// a

// a is not a valid number. Please try again

// Program complete.

Of course, there are still situations in which a finally block won’t run. For example, when the try block goes into an infinite loop, it will never exit the try block and never enter the finally block. And in situations such as a power outage, no other code will run. The whole operating system will just terminate.

There is one other situation that you can use to prevent a finally block from running. Of course, this isn’t something you want to use on a regular basis, but you may have a situation in which just shutting down the application is safer than running finally blocks.

Preventing the finally block from running can be achieved by using Environment.FailFast. This method has two different overloads, one that only takes a string and another one that also takes an exception. When this method is called, the message (and optionally the exception) are written to the Windows application event log, and the application is terminated. Example 1-92 shows how you can use this method. When you run this application without a debugger attached, a message is written to the event log.

Example 1-92. Using Environment.FailFast

using System;

namespace ExceptionHandling

{

public static class Program

{

public static void Main()

{

string s = Console.ReadLine();

try

{

int i = int.Parse(s);

if (i == 42) Environment.FailFast("Special number entered");

}

finally

{

Console.WriteLine("Program complete.");

}

}

}

}

The line Program Complete won’t be executed if 42 is entered. Instead the application shuts down immediately.

When you catch an exception, you can use a couple of properties to inspect what’s happened. Table 1-3 lists the properties of the base System.Exception class.

Table 1-3. System.Exception properties

Name

Description

StackTrace

A string that describes all the methods that are currently in execution. This gives you a way of tracking which method threw the exception and how that method was reached.

InnerException

When a new exception is thrown because another exception happened, the two are linked together with the InnerException property.

Message

A (hopefully) human friendly message that describes the exception.

HelpLink

A Uniform Resource Name (URN) or uniform resource locater (URL) that points to a help file.

HResult

A 32-bit value that describes the severity of an error, the area in which the exception happened and a unique number for the exception This value is used only when crossing managed and native boundaries.

Source

The name of the application that caused the error. If the Source is not explicitly set, the name of the assembly is used.

TargetSite

Contains the name of the method that caused the exception. If this data is not available, the property will be null.

Data

A dictionary of key/value pairs that you can use to store extra data for your exception. This data can be read by other catch blocks and can be used to control the processing of the exception.

When using a catch block, you can use both an exception type and a named identifier. This way, you effectively create a variable that will hold the exception for you so you can inspect its properties. Example 1-93 shows how to do this.

Example 1-93. Inspecting an exception

using System;

namespace ExceptionHandling

{

public static class Program

{

public static void Main()

{

try

{

int i = ReadAndParse();

Console.WriteLine("Parsed: {0}", i);

}

catch (FormatException e)

{

Console.WriteLine("Message: {0}",e.Message);

Console.WriteLine("StackTrace: {0}", e.StackTrace);

Console.WriteLine("HelpLink: {0}", e.HelpLink);

Console.WriteLine("InnerException: {0}", e.InnerException);

Console.WriteLine("TargetSite: {0}", e.TargetSite);

Console.WriteLine("Source: {0}", e.Source);

}

}

private static int ReadAndParse()

{

string s = Console.ReadLine();

int i = int.Parse(s);

return i;

}

}

}

//Displays

//Message: Input string was not in a correct format.

//StackTrace: at System.Number.StringToNumber(String str, NumberStyles options,

// NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)

// at System.Number.ParseInt32(String s, NumberStyles style,

// NumberFormatInfo info)

// at System.Int32.Parse(String s)

// at ExceptionHandling.Program.ReadAndParse() in

// c:\Users\Wouter\Documents\Visual Studio 2012\Projects\

// ExamRefProgrammingInCSharp\Chapter1\Program.cs:line 27

// at ExceptionHandling.Program.Main() in c:\Users\Wouter\Documents\

// Visual Studio 2012\Projects\ExamRefProgrammingInCSharp\

// Chapter1\Program.cs:line 10

// HelpLink:

// InnerException:

// TargetSite: Void StringToNumber(System.String, System.Globalization.NumberStyles

// , NumberBuffer ByRef, System.Globalization.NumberFormatInfo, Boolean)

// Source: mscorlib

It’s important to make sure that your finally block does not cause any exceptions. When this happens, control immediately leaves the finally block and moves to the next outer try block, if any. The original exception is lost and you can’t access it anymore.

You should only catch an exception when you can resolve the issue or when you want to log the error. Because of this, it’s important to avoid general catch blocks at the lower layers of your application. This way, you could accidentally swallow an important exception without even knowing that it happened. Logging should also be done somewhere higher up in your application. That way, you can avoid logging duplicate errors at multiple layers in your application.

Throwing exceptions

When you want to throw an error, you first need to create a new instance of an exception. You can then use the special throw keyword to throw the exception. After this, the runtime will start looking for catch and finally blocks.

Example 1-94 shows how you can throw an exception.

Example 1-94. Throwing an ArgumentNullException

public static string OpenAndParse(string fileName)

{

if (string.IsNullOrWhiteSpace(fileName))

throw new ArgumentNullException("fileName", "Filename is required");

return File.ReadAllText(fileName);

}

You should not try to reuse exception objects. Each time you throw an exception, you should create a new one, especially when working in a multithreaded environment, the stack trace of your exception can be changed by another thread.

When catching an exception, you can choose to rethrow the exception. You have three ways of doing this:

§ Use the throw keyword without an identifier.

§ Use the throw keyword with the original exception.

§ Use the throw keyword with a new exception.

The first option rethrows the exception without modifying the call stack. This option should be used when you don’t want any modifications to the exception. Example 1-95 shows an example of using this mechanism.

Example 1-95. Rethrowing an exception

try

{

SomeOperation();

}

catch (Exception logEx)

{

Log(logEx);

throw; // rethrow the original exception

}

When you choose the second option, you reset the call stack to the current location in code. So you can’t see where the exception originally came from, and it is harder to debug the error.

Using the third option can be useful when you want to raise another exception to the caller of your code.

Say, for example, that you are working on an order application. When a user places an order, you immediately put this order in a message queue so another application can process it.

When an internal error happens in the message queue, an exception of type Message-QueueException is raised. To users of your ordering application, this exception won’t make any sense. They don’t know the internal workings of your module and they don’t understand where the message queue error is coming from.

Instead, you can throw another exception, something like a custom OrderProcessingException, and set the InnerException to the original exception. In your OrderProcessingException you can put extra information for the user of your code to place the error in context and help them solve it. Example 1-96 shows an example. The original exception is preserved, including the stack trace, and a new exception with extra information is added.

Example 1-96. Throwing a new exception that points to the original one

try

{

ProcessOrder();

}

catch (MessageQueueException ex)

{

throw new OrderProcessingException("Error while processing order", ex);

}

EXAM TIP

Make sure that you don’t swallow any exception details when rethrowing an exception. Throw a new exception that points to the original one when you want to add extra information; otherwise, use the throw keyword without an identifier to preserve the original exception details.

In C# 5, a new option is added for rethrowing an exception. You can use the ExceptionDispatchInfo.Throw method, which can be found in the System.Runtime.ExceptionServices namespace. This method can be used to throw an exception and preserve the original stack trace. You can use this method even outside of a catch block, as shown in Example 1-97.

Example 1-97. Using ExceptionDispatchInfo.Throw

ExceptionDispatchInfo possibleException = null;

try

{

string s = Console.ReadLine();

int.Parse(s);

}

catch (FormatException ex)

{

possibleException = ExceptionDispatchInfo.Capture(ex);

}

if (possibleException != null)

{

possibleException.Throw();

}

// Displays

// Unhandled Exception: System.FormatException:

// Input string was not in a correct format.

// at System.Number.StringToNumber(String str, NumberStyles options,

// NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)

// at System.Number.ParseInt32(String s, NumberStyles style,

// NumberFormatInfo info)

// at System.Int32.Parse(String s)

// at ExceptionHandling.Program.Main() in c:\Users\Wouter\Documents\

// Visual Studio 2012\Projects\ExamRefProgrammingInCSharp\Chapter1\

// Program.cs:line 17

//--- End of stack trace from previous location where exception was thrown ---

// at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()

// at ExceptionHandling.Program.Main() in c:\Users\Wouter\Documents\

// Visual Studio 2012\Projects\ExamRefProgrammingInCSharp\Chapter1\

// Program.cs:line 6

When looking at the stack trace, you see this line, which shows where the original exception stack trace ends and the ExceptionDispatchInfo.Throw is used:

--- End of stack trace from previous location where exception was thrown ---

This feature can be used when you want to catch an exception in one thread and throw it on another thread. By using the ExceptionDispatchInfo class, you can move the exception data between threads and throw it. The .NET Framework uses this when dealing with the async/await feature added in C# 5. An exception that’s thrown on an async thread will be captured and rethrown on the executing thread.

MORE INFO: THREADS AND ASYNC/AWAIT

For more information on working with threads and the new async/await feature, see the section titled Objective 1.1: Implement multithreading and asynchronous processing earlier in this chapter.

You shouldn’t throw exceptions when dealing with expected situations. You know that when users start entering information into your application, they will make mistakes. Maybe they enter a number in the wrong format or forget to enter a required field. Raising an exception for these kinds of expected situations is not recommended.

Exception handling changes the normal expected flow of your program. This makes it harder to read and maintain code that uses exceptions, especially when they are used in normal situations.

Using exceptions also incurs a slight performance hit. Because the runtime has to search all outer catch blocks until it finds a matching block, and when it doesn’t, has to look if a debugger is attached, it takes slightly more time to handle. When a real unexpected situation occurs that will terminate the application, this won’t be a problem. But for regular program flow, it should be avoided. Instead you should have proper validation and not rely solely on exceptions.

When you need to throw an exception of your own, it’s important to know which exceptions are already defined in the .NET Framework. Because developers will be familiar with these exceptions, they should be used whenever possible.

Some exceptions are thrown only by the runtime. You shouldn’t use those exceptions from your own code. Table 1-4 lists those exceptions.

Table 1-4. Runtime exceptions in the .NET Framework

Name

Description

ArithmeticException

A base class for other exceptions that occur during arithmetic operations.

ArrayTypeMismatchException

Thrown when you want to store an incompatible element inside an array.

DivideByZeroException

Thrown when you try to divide a value by zero.

IndexOutOfRangeException

Thrown when you try to access an array with an index that’s less than zero or greater than the size of the array.

InvalidCastException

Thrown when you try to cast an element to an incompatible type.

NullReferenceException

Thrown when you try to reference an element that’s null.

OutOfMemoryException

Thrown when creating a new object fails because the CLR doesn’t have enough memory available.

OverflowException

Thrown when an arithmetic operation overflows in a checked context.

StackOverflowException

Thrown when the execution stack is full. This can happen in a recursive operation that doesn’t exit.

TypeInitializationException

Thrown when a static constructor throws an exception that’s goes unhandled.

You shouldn’t throw these exceptions in your own applications. Table 1-5 shows popular exceptions in the .NET Framework that you can use.

Table 1-5. Popular exceptions in the .NET Framework

Name

Description

Exception

The base class for all exceptions. Try avoiding throwing and catching this exception because it’s too generic.

ArgumentException

Throw this exception when an argument to your method is invalid.

ArgumentNullException

A specialized form of ArgumentException that you can throw when one of your arguments is null and this isn’t allowed.

ArgumentOutOfRangeException

A specialized form of ArgumentException that you can throw when an argument is outside the allowable range of values.

FormatException

Throw this exception when an argument does not have a valid format.

InvalidOperationException

Throw this exception when a method is called that’s invalid for the object’s current state.

NotImplementedException

This exception is often used in generated code where a method has not been implemented yet.

NotSupportedException

Throw this exception when a method is invoked that you don’t support.

ObjectDisposedException

Throw when a user of your class tries to access methods when Dispose has already been called.

You should avoid directly using the Exception base class both when catching and throwing exceptions. Instead you should try to use the most specific exception available.

Creating custom exceptions

Once throwing an exception becomes necessary, it’s best to use the exceptions defined in the .NET Framework. But there are situations in which you want to use a custom exception. This is especially useful when developers working with your code are aware of those exceptions and can handle them in a more specific way than the framework exceptions.

A custom exception should inherit from System.Exception. You need to provide at least a parameterless constructor. It’s also a best practice to add a few other constructors: one that takes a string, one that takes both a string and an exception, and one for serialization. Example 1-98 shows an example of a custom exception.

Example 1-98. Creating a custom exception

[Serializable]

public class OrderProcessingException : Exception, ISerializable

{

public OrderProcessingException(int orderId)

{

OrderId = orderId;

this.HelpLink = "http://www.mydomain.com/infoaboutexception";

}

public OrderProcessingException(int orderId, string message)

: base(message)

{

OrderId = orderId;

this.HelpLink = "http://www.mydomain.com/infoaboutexception";

}

public OrderProcessingException(int orderId, string message,

Exception innerException)

: base(message, innerException)

{

OrderId = orderId;

this.HelpLink = "http://www.mydomain.com/infoaboutexception";

}

protected OrderProcessingException(SerializationInfo info, StreamingContext context)

{

OrderId = (int)info.GetValue("OrderId", typeof(int));

}

public int OrderId { get; private set; }

public void GetObjectData(SerializationInfo info, StreamingContext context)

{

info.AddValue("OrderId", OrderId, typeof(int));

}

}

By convention, you should use the Exception suffix in naming all your custom exceptions. It’s also important to add the Serializable attribute, which makes sure that your exception can be serialized and works correctly across application domains (for example, when a web service returns an exception).

When creating your custom exception, you can decide which extra data you want to store. Exposing this data through properties can help users of your exception inspect what has gone wrong.

You should never inherit from System.ApplicationException. The original idea was that all C# runtime exceptions should inherit from System.Exception and all custom exceptions from System.ApplicationException. However, because the .NET Framework doesn’t follow this pattern, the class became useless and lost its meaning.

THOUGHT EXPERIMENT

Planning your error strategy

In this thought experiment, apply what you’ve learned about this objective. You can find answers to these questions in the Answers section at the end of this chapter.

You are designing a new application and you want to implement a proper error-handling strategy. You are discussing the topic with some colleagues, and one of them says that you should use regular error codes to signal errors because that’s faster and your company has used it in the past.

You are also having a discussion about when to create a custom exception and when to use the built-in .NET Framework exceptions.

1. Explain to your colleague the advantages of Exceptions compared to error codes.

2. When should you create a custom exception?

Objective summary

§ In the .NET Framework, you should use exceptions to report errors instead of error codes.

§ Exceptions are objects that contain data about the reason for the exception.

§ You can use a try block with one or more catch blocks to handle different types of exceptions.

§ You can use a finally block to specify code that should always run after, whether or not an exception occurred.

§ You can use the throw keyword to raise an exception.

§ You can define your own custom exceptions when you are sure that users of your code will handle it in a different way. Otherwise, you should use the standard .NET Framework exceptions.

Objective review

Answer the following questions to test your knowledge of the information in this objective. You can find the answers to these questions and explanations of why each answer choice is correct or incorrect in the Answers section at the end of this chapter.

1. You are checking the arguments of your method for illegal null values. If you encounter a null value, which exception do you throw?

a. ArgumentException.

b. InvalidOperationException.

c. NullReferenceException.

d. ArgumentNullException.

2. Your code catches an IOException when a file cannot be accessed. You want to give more information to the caller of your code. What do you do?

a. Change the message of the exception and rethrow the exception.

b. Throw a new exception with extra information that has the IOException as InnerException.

c. Throw a new exception with more detailed info.

d. Use throw to rethrow the exception and save the call stack.

3. You are creating a custom exception called LogonFailedException. Which constructors should you at least add? (Choose all that apply.)

a. LogonFailed()

b. LogonFailed(string message)

c. LogonFailed(string message, Exception innerException)

d. LogonFailed(Exception innerException)

Chapter summary

§ Multithreading can help you create programs that are responsive and scalable. You can use the TPL, the Parallel class, and PLINQ for this. The new async/await keywords help you write asynchronous code.

§ In a multithreaded environment, it’s important to manage synchronization of shared data. You can do this by using the lock statement.

§ C# offers statements for making decisions—if, switch, conditional operator (?) and null-coalescing operator (??)—iterating (for, foreach, while, do-while), and jump statements (break, continue, goto, return and throw).

§ Delegates are objects that point to a method and can be used to invoke the method. Lambda expressions are a shorthand syntax for creating anonymous methods inline.

§ Events are a layer on top of delegates that help you with creating a publish-subscribe architecture.

§ Exceptions are the preferred way to work with errors in the .NET Framework. You can throw exceptions, catch them, and run code in a finally block.

Answers

This section contains the solutions to the thought experiments and answers to the lesson review questions in this chapter.

Objective 1.1: Thought experiment

1. Multithreading can improve the responsiveness in a client application. The UI thread can process requests from the user while background threads execute other operations.

2. A CPU-bound operation needs a thread to execute. In a client application, it can make sense to execute a CPU-bound operation on another thread to improve responsiveness. In a server application, you don’t want an extra thread for a CPU-bound operation. Asynchronous I/O operations don’t require a thread while executing. Using asynchronous I/O frees the current thread to do other work and improves scalability.

3. Using multithreading in a server environment can help you distribute operations over multiple CPUs. This way, you can improve performance. Using the TPL to create another thread to execute a CPU-bound operation while the originating thread has to wait for it won’t help you with increasing performance.

Objective 1.1: Review

1. Correct answer: B

a. Incorrect: Manually creating and managing tasks is not necessary. The Parallel class takes care of this and uses the optimal configuration options.

b. Correct: Parallel. For is ideal for executing parallel operations on a large set of items that have to do a lot of work.

c. Incorrect: async/await does not process items concurrently. Instead it waits until the current task has finished and then continues executing the code.

d. Incorrect: The BlockingCollection can be used to share data between multiple threads. Using one producer and one consumer thread, however, won’t improve scalability. The Parallel class is designed for this scenario and should be used.

2. Correct answer: A

a. Correct: AsParallel makes a sequential query parallel if the runtime thinks this will improve performance.

b. Incorrect: AsSequential is used to make a parallel query sequential again.

c. Incorrect: AsOrdered is used to make sure that the results of a parallel query are returned in order.

d. Incorrect: WithDegreeOfParallelism is used to specify how many threads the parallel query should use.

3. Correct answer: C

a. Incorrect: Because you have to wait for external factors (the database and web response), you should use async/await to free your thread. That way your thread can do some other work while waiting for the external responses to come back.

b. Incorrect: Async/await can be used to improve responsiveness on the client but it can also be used in server scenarios. Especially when waiting for an I/O-bound operation, you can use asynchronous code to free the thread from waiting.

c. Correct: The operating system waits for the I/O request to complete and then activates a thread that can process the response. In the meantime, the thread can do other work.

d. Incorrect: Async/await does not put your thread to sleep in an I/O-bound situation. Instead, your thread can process other work while the operating system monitors the status of the request. When the request finishes, a thread is used to process the response. With a CPU-bound operation, your thread waits for the operation to finish on another thread.

Objective 1.2: Thought experiment

1. It’s important to make sure that all locking follows the same order when locking multiple objects. As soon as you start locking dependent objects in different orders, you start getting deadlocks.

2. The Interlocked class can help you to execute small, atomic operations without the need for locking. When you use locking a lot for these kind of operations, you can replace them with the Interlocked statement.

Objective 1.2: Review

1. Correct answer: D

a. Incorrect: You should never lock on this. Another part of your code may already be using your object to execute a lock.

b. Incorrect: You shouldn’t use a string for locking. With string-interning, one object can be used for multiple strings, so you would be locking on an object that is also in use in other locations.

c. Incorrect: Locking on a value type will generate a compile error. The value type will be boxed each time you lock on it, resulting in a unique lock each time.

d. Correct: A private lock of type object is the best choice.

2. Correct answer: B

a. Incorrect: The CancellationTokenSource is used to generate a CancellationToken. The token should be passed to the task, and the CancellationTokenSource can then be used to request cancellation on the token.

b. Correct: A CancellationToken generated by a CancellationTokenSource should be passed to the task.

c. Incorrect: A Boolean variable can be used to cancel a task, but it’s not the preferred way. A CancellationToken offers more flexibility and should be used.

d. Incorrect: The volatile keyword should be used to signal to the compiler that the order of reads and writes on a field is important and that the compiler shouldn’t change it.

3. Correct answer: B

a. Incorrect: Volatile.Write is used to signal to the compiler that writing the value to a field should happen at that exact location.

b. Correct: CompareExchange will see whether the current state is correct and it will then change it to the new state in one atomic operation.

c. Incorrect: Exchange only changes the value; it doesn’t check to see whether the current state is correct.

d. Incorrect: Decrement is used to subtract one off the value in an atomic operation.

Objective 1.3: Thought experiment

1. Using the goto statement makes your code much harder to read because the application flow jumps around. goto is mostly used in looping statements. You can then replace goto with while or do-while.

2. The switch statement can be used to improve long if statements.

3. The for statement can be used to iterate over a collection by using an index. You can modify the collection while iterating. You need to use the index to retrieve each item. foreach is syntactic sugar over the iterator pattern. You don’t use an index; instead the compiler gives you a variable that points to each iteration item.

Objective 1.3: Review

1. Correct answer: C

a. Incorrect: switch is used as a decision statement. You map a value to certain labels to execute specific code; it doesn’t iterate over collections.

b. Incorrect: Although the foreach statement can be used to iterate over a collection; it doesn’t allow changes to the collection while iterating.

c. Correct: With for, you can iterate over the collection while modifying it. It’s your own job to make sure that the index stays correct.

d. Incorrect: goto is a jump statement that should be avoided.

2. Correct answer: D

a. Incorrect: for is an iteration statement that can’t be used to check for null values.

b. Incorrect: The conditional operator can be used to shorten if statements. It’s not useful to conditionally call a method.

c. Incorrect: The null-coalescing operator does check for null values but it’s used to provide a default value. It’s not useful when calling a method if the value is not null.

d. Correct: Short-circuiting enables you to see whether a value is null and call a member on it in one and statement. If the left value is null, the right operand won’t be executed.

3. Correct answer: C

a. Incorrect: A for statement is most useful when iterating over a collection in which you know the number of items beforehand.

b. Incorrect: foreach can be used only on types that implement IEnumerable. It can’t be easily used with your two custom methods.

c. Correct: You can use while (o.HasNext) { var i = o.Read(); } to process the items. When o.HasNext returns false, you automatically end the loop.

d. Incorrect: Do-while will run the code at least once. If there are no items on the network, the code doesn’t have to run.

Objective 1.4: Thought experiment

1. Events are a nice layer on top of delegates that make them easier and safer to use. In this case, you should use events to make sure that other users won’t be able to clear all subscriptions. It also makes sure that they can’t raise the event on their own. They can only listen to changes.

2. The advantage of using an event system in an application like this is that you can achieve loose coupling. Your forms don’t have to know anything about each other. The class that monitors data changes and raises the event doesn’t have to know how many forms are listening and how they look. Third-party plug-ins can easily subscribe to the events at runtime to be able to respond to changes without tightly coupling to the existing system.

Objective 1.4: Review

1. Correct answer: C

a. Incorrect: Making the method public gives access to all users of your class.

b. Incorrect: This doesn’t give users of your class the ability to execute the method.

c. Correct: The method can see whether the caller is authorized and then return a delegate to the private method that can be invoked at will.

d. Incorrect: Changing the method to a lambda doesn’t change the fact that outside users can’t access the method.

2. Correct answer: B

a. Incorrect: The compiler restricts the use of events outside of the class where it’s defined. They can only add and remove subscribers. Only the class itself can raise the event.

b. Correct: The public method can be called by outside users of your class. Internally it can raise the event.

c. Incorrect: Using a delegate does allow it to be invoked from outside the class. However, you lose the protection that an event gives you. A public delegate can be completely modified by outside users without any restrictions.

d. Incorrect: Canonical name (CNAME) records map an alias or nickname to the real or canonical name that might lie outside the current zone.

3. Correct answer: A

a. Correct: You can handle each individual error and make sure that all subscribers are called.

b. Incorrect: Wrapping the raising of the event in one try/catch will still cause the invocation to stop at the first exception. Later subscribers won’t be notified.

c. Incorrect: By default, the invocation of subscribers stops when the first unhandled exception happens in one of the subscribers.

d. Incorrect: Exceptions are the preferred way of dealing with errors. Returning a value from each event still requires you to invoke them manually one by one to check the return value.

Objective 1.5: Thought experiment

1. Exceptions are objects, so they can store extra information that can’t be done with only an error code. The .NET Framework also offers special support for dealing with exceptions. For example, you can use catch blocks to handle certain types of exceptions and you can use a finally block to make sure that certain code will always run.

2. You should create a custom exception only if you expect developers to handle it or perform custom logging. If a developer won’t be able to fix the specific error, it won’t make any sense to create a more specific exception. Custom logging can happen when you throw more detailed exceptions, so developers can differentiate between the errors that happen.

Objective 1.5: Review

1. Correct answer: D

a. Incorrect: Although the exception has to do with an argument to your method, you should throw the more specialized ArgumentNullException.

b. Incorrect: InvalidOperationException should be used when your class is not in the correct state to handle a request.

c. Incorrect: NullReferenceException is thrown by the runtime when you try to reference a null value.

d. Correct: ArgumentNullException is the most specialized exception that you can use to tell which argument was null and what you expect.

2. Correct answer: B

a. Incorrect: The Message property of an exception is read-only. You can’t change it after the exception is created.

b. Correct: The new exception can contain extra info. Setting the InnerException makes sure that the original exception stays available.

c. Incorrect: Throwing a brand-new exception loses the original exception and the information that it had.

d. Incorrect: Using throw without an identifier will rethrow the original exception while maintaining the stack trace, but it won’t add any extra information.

3. Correct answers: A, B, C

a. Correct: You should always add a default empty constructor.

b. Correct: A second constructor should take a descriptive message of why the error occurred.

c. Correct: An InnerException can be set to correlate two exceptions and show what the original error was.

d. Incorrect: You don’t have to define a constructor that only takes an InnerException without a message.