Tải bản đầy đủ - 0 (trang)
3-18. Create a Custom Dynamic Type

3-18. Create a Custom Dynamic Type

Tải bản đầy đủ - 0trang


Table 3-4. Useful Methods from the System.Dynamic.DynamicObject Class




Called for binary operations such as addition and multiplication


Called for operations that convert from one type to another


Called when the type is instantiated


Called when a value is requested via an array-style index


Called when a value is requested via a property


Called when a method is invoked


Called when a value is set via an array-style index


Called when a property value is set

Each of the TryXXX methods defines arguments that allow you to determine what member the caller

has called and the arguments or values that have been passed to you. For example, to implement custom

behavior for getting a property value, we would implement the TryGetMember method, which is declared

as follows:

public override bool TryGetMember(GetMemberBinder binder, out object result)

The GetMemberBinder class provides us with information about the property that the caller has

requested—the most useful member being Name, which returns the name of the property that the caller

wants—remember that dynamic types allow the caller to request any property, not just the ones we have

implemented. You set the value of the result parameter to whatever you want to return to the caller and

use the bool returned from the method to indicate whether you are willing to support the property that

has been requested. For example, the following fragment uses the GetMemberBinder parameter to

determine which property the caller has asked for, and will only return a value for those properties that

begin with the letter a:

public override bool TryGetMember(GetMemberBinder binder, out object result)


if (binder.Name.StartsWith("A"))


result = "Hello";

return true;

} else {

result = null;

return false;






Returning false from a TryXXX method will cause a runtime exception to be thrown to the caller—

see the example code for this recipe for an example of this. Note that you must assign a value to the

result parameter even if you are returning false from the method.

You must declare an instance of your type using the dynamic keyword—if you do not, the compiler

will perform static checking, and you will only be able to access the members you have defined and the

TryXXX members of the DynamicObject class.

The Code

The following example extends the DynamicObject class to create a wrapper around a dictionary such

that calls to get and set properties on the dynamic type are mapped to the key/value pairs contained in

the dictionary:













namespace Apress.VisualCSharpRecipes.Chapter03


class Recipe03_18


static void Main(string[] args)


dynamic dynamicDict = new MyDynamicDictionary();

// Set some properties.

Console.WriteLine("Setting property values");

dynamicDict.FirstName = "Adam";

dynamicDict.LastName = "Freeman";

// Get some properties.

Console.WriteLine("\nGetting property values");

Console.WriteLine("Firstname {0}", dynamicDict.FirstName);

Console.WriteLine("Lastname {0}", dynamicDict.LastName);

// Call an implemented member.

Console.WriteLine("\nGetting a static property");

Console.WriteLine("Count {0}", dynamicDict.Count);

Console.WriteLine("\nGetting a non-existent property");



Console.WriteLine("City {0}", dynamicDict.City);





catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException e)


Console.WriteLine("Caught exception");


// Wait to continue.

Console.WriteLine("\nMain method complete. Press Enter.");




class MyDynamicDictionary : DynamicObject


private IDictionary dict = new Dictionary();

public int Count




Console.WriteLine("Get request for Count property");

return dict.Count;



public override bool TryGetMember(GetMemberBinder binder, out object result)


Console.WriteLine("Get request for {0}", binder.Name);

return dict.TryGetValue(binder.Name, out result);


public override bool TrySetMember(SetMemberBinder binder, object value)


Console.WriteLine("Set request for {0}, value {1}", binder.Name, value);

dict[binder.Name] = value;

return true;







Running the example gives the following results:

Setting property values

Set request for FirstName, value Adam

Set request for LastName, value Freeman

Getting property values

Get request for FirstName

Firstname Adam

Get request for LastName

Lastname Freeman

Getting a static property

Get request for Count property

Count 2

Getting a non-existent property

Get request for City

Caught exception

Main method complete. Press Enter.






Threads, Processes, and


One of the strengths of the Microsoft Windows operating system is that it allows many programs

(processes) to run concurrently and allows each process to perform many tasks concurrently (using

multiple threads). When you run an executable application, a new process is created. The process

isolates your application from other programs running on the computer. The process provides the

application with its own virtual memory and its own copies of any libraries it needs to run, allowing your

application to execute as if it were the only application running on the machine.

Along with the process, an initial thread is created that runs your Main method. In single-threaded

applications, this one thread steps through your code and sequentially performs each instruction. If an

operation takes time to complete, such as reading a file from the Internet or doing a complex

calculation, the application will be unresponsive (will block) until the operation is finished, at which

point the thread will continue with the next operation in your program.

To avoid blocking, the main thread can create additional threads and specify which code each

should start running. As a result, many threads may be running in your application’s process, each

running (potentially) different code and performing different operations seemingly simultaneously. In

reality, unless you have multiple processors (or a single multicore processor) in your computer, the

threads are not really running simultaneously. Instead, the operating system coordinates and schedules

the execution of all threads across all processes; each thread is given a tiny portion (or time slice) of the

processor’s time, which gives the impression they are executing at the same time.

The difficulty of having multiple threads executing within your application arises when those

threads need to access shared data and resources. If multiple threads are changing an object’s state or

writing to a file at the same time, your data will quickly become corrupt. To avoid problems, you must

synchronize the threads to make sure they each get a chance to access the resource, but only one at a

time. Synchronization is also important when waiting for a number of threads to reach a certain point of

execution before proceeding with a different task, and for controlling the number of threads that are at

any given time actively performing a task—perhaps processing requests from client applications.

.NET 4.0 includes two mechanisms for creating multithreaded applications. The “classic” approach

is the one that has been included in .NET since version 1.0, in which the programmer takes

responsibility for creating and managing threads directly. The Task Parallel Library is newly added and

manages threads automatically but reduces the developer’s control over the application.

For information about the Task Parallel Library, please see Chapter 15. This chapter describes how

to control processes and threads using the classic techniques, which are more complex, but offer a

greater degree of control. Specifically, the recipes in this chapter describe how to do the following:




Execute code in independent threads using features including the thread pool,

asynchronous method invocation, and timers (recipes 4-1 through 4-6)

Synchronize the execution of multiple threads using a host of synchronization

techniques including monitors, events, mutexes, and semaphores (recipes 4-7 and


Terminate threads and know when threads have terminated (recipes 4-12 and 413)

Create thread-safe instances of the .NET collection classes (recipe 4-14)

Start and stop applications running in new processes (recipes 4-15 and 4-16)

Ensure that only one instance of an application is able to run at any given time

(recipe 4-17)

As you will see in this chapter, delegates are used extensively in multithreaded programs to wrap the

method that a thread should execute or that should act as a callback when an asynchronous operation is

complete. Prior to C# 2.0, it was necessary to


Declare a method that matches the signature of the required delegate


Create a delegate instance of the required type by passing it the name of the



Pass the delegate instance to the new thread or asynchronous operation

C# 2.0 added two important new features that simplify the code you must write when using


First, you no longer need to create a delegate instance to wrap the method you

want to execute. You can pass a method name where a delegate is expected, and

as long as the method signature is correct, the compiler infers the need for the

delegate and creates it automatically. This is a compiler enhancement only—the

intermediate language (IL) generated is as if the appropriate delegate had been

instantiated. Recipes 4-1 and 4-2 (along with many others) demonstrate how to

use this capability.

Second, you no longer need to explicitly declare a method for use with the

delegate. Instead, you can provide an anonymous method wherever a delegate is

required. In effect, you actually write the method code at the point where you

would usually pass the method name (or delegate instance). The only difference is

that you use the keyword delegate instead of giving the method a name. This

approach can reduce the need to implement methods solely for use as callbacks

and event handlers, which reduces code clutter, but it can quickly become

confusing if the anonymous method is longer than a couple of lines of code.

Recipes 4-3 and 4-4 demonstrate how to use anonymous methods.




4-1. Execute a Method Using the Thread Pool


You need to execute a task using a thread from the runtime’s thread pool.


Declare a method containing the code you want to execute. The method’s signature must match that

defined by the System.Threading.WaitCallback delegate; that is, it must return void and take a single

object argument. Call the static method QueueUserWorkItem of the System.Threading.ThreadPool class,

passing it your method name. The runtime will queue your method and execute it when a thread-pool

thread becomes available.

How It Works

Applications that use many short-lived threads or maintain large numbers of concurrent threads can

suffer performance degradation because of the overhead associated with the creation, operation, and

destruction of threads. In addition, it is common in multithreaded systems for threads to sit idle a large

portion of the time while they wait for the appropriate conditions to trigger their execution. Using a

thread pool provides a common solution to improve the scalability, efficiency, and performance of

multithreaded systems.

The .NET Framework provides a simple thread-pool implementation accessible through the

members of the ThreadPool static class. The QueueUserWorkItem method allows you to execute a method

using a thread-pool thread by placing a work item on a queue. As a thread from the thread pool becomes

available, it takes the next work item from the queue and executes it. The thread performs the work

assigned to it, and when it is finished, instead of terminating, the thread returns to the thread pool and

takes the next work item from the work queue.

■ Tip If you need to execute a method with a signature that does not match the WaitCallback delegate, then you

must use one of the other techniques described in this chapter. See recipe 4-2 or 4-6.

The Code

The following example demonstrates how to use the ThreadPool class to execute a method named

DisplayMessage. The example passes DisplayMessage to the thread pool twice, first with no arguments

and then with a MessageInfo object, which allows you to control which message the new thread will





using System;

using System.Threading;

namespace Apress.VisualCSharpRecipes.Chapter04


class Recipe04_01


// A private class used to pass data to the DisplayMessage method when it is

// executed using the thread pool.

private class MessageInfo


private int iterations;

private string message;

// A constructor that takes configuration settings for the thread.

public MessageInfo(int iterations, string message)


this.iterations = iterations;

this.message = message;


// Properties to retrieve configuration settings.

public int Iterations { get { return iterations; } }

public string Message { get { return message; } }


// A method that conforms to the System.Threading.WaitCallback delegate

// signature. Displays a message to the console.

public static void DisplayMessage(object state)


// Safely cast the state argument to a MessageInfo object.

MessageInfo config = state as MessageInfo;

// If the config argument is null, no arguments were passed to

// the ThreadPool.QueueUserWorkItem method; use default values.

if (config == null)


// Display a fixed message to the console three times.

for (int count = 0; count < 3; count++)


Console.WriteLine("A thread pool example.");

// Sleep for the purpose of demonstration. Avoid sleeping

// on thread-pool threads in real applications.








Tài liệu bạn tìm kiếm đã sẵn sàng tải về

3-18. Create a Custom Dynamic Type

Tải bản đầy đủ ngay(0 tr)