Friday, September 7, 2007

Boxing and Unboxing


Boxing and Unboxing enable value types to be treated as objects. Value types, including both struct types and built-in types, such as int,

can be converted to and from the type object

Boxing is an implicit conversion of a value type the type object or to any interface type implemented by this value type. Boxing a value of a value allocates an object instance and copies the value into the new object.

Consider the following declaration of a value-type variable:

int i = 123;

The following statement implicitly applies the boxing operation on the variable i:

object o = i;

The result of this statement is creating an object o, on the stack, that references a value of the type int, on the heap. This value is a copy of the value-type value assigned to the variable i. The difference between the two variables, i and o, is illustrated in the following figure.

Boxing Conversion

FPRIVATE "TYPE=PICT;ALT="

It also possible, but never needed, to perform the boxing explicitly as in the following example:

int i = 123;

object o = (object) i;

Example

This example converts an integer variable i to an object o via boxing. Then the value stored in the variable i is changed from 123 to 456. The example shows that the object keeps the original copy of the contents, 123.

// boxing.cs

// Boxing an integer variable

using System;

class TestBoxing

{

public static void Main()

{

int i = 123;

object o = i; // Implicit boxing

i = 456; // Change the contents of i

Console.WriteLine("The value-type value = {0}", i);

Console.WriteLine("The object-type value = {0}", o);

}

}

Output

The value-type value = 456

The object-type value = 123

A boxing conversion permits a value-type to be implicitly converted to a reference-type. The following boxing conversions exist:

From any value-type (including any enum-type) to the type object.

From any value-type (including any enum-type) to the type System.ValueType.

From any value-type to any interface-type implemented by the value-type.

From any enum-type to the type System.Enum.

Boxing a value of a value-type consists of allocating an object instance and copying the value-type value into that instance.

The actual process of boxing a value of a value-type is best explained by imagining the existence of a boxing class for that type. For any value-type T, the boxing class behaves as if it were declared as follows:

sealed class T_Box: System.ValueType

{

T value;

public T_Box(T t) {

value = t;

}

}

Boxing of a value v of type T now consists of executing the expression new T_Box(v), and returning the resulting instance as a value of type object. Thus, the statements

int i = 123;

object box = i;

conceptually correspond to

int i = 123;

object box = new int_Box(i);

Boxing classes like T_Box and int_Box above do not actually exist and the dynamic type of a boxed value is not actually a class type. Instead, a boxed value of type T has the dynamic type T, and a dynamic type check using the is operator can simply reference type T. For example,

int i = 123;

object box = i;

if (box is int) {

Console.Write("Box contains an int");

}

will output the string "Box contains an int" on the console.

A boxing conversion implies making a copy of the value being boxed. This is different from a conversion of a reference-type to type object, in which the value continues to reference the same instance and simply is regarded as the less derived type object. For example, given the declaration

struct Point

{

public int x, y;

public Point(int x, int y) {

this.x = x;

this.y = y;

}

}

the following statements

Point p = new Point(10, 10);

object box = p;

p.x = 20;

Console.Write(((Point)box).x);

will output the value 10 on the console because the implicit boxing operation that occurs in the assignment of p to box causes the value of p to be copied. Had Point been declared a class instead, the value 20 would be output because p and box would reference the same instance.

Unboxing

C# Language Specification

4.3.2 Unboxing conversions

An unboxing conversion permits a reference-type to be explicitly converted to a value-type. The following unboxing conversions exist:

From the type object to any value-type (including any enum-type).

From the type System.ValueType to any value-type (including any enum-type).

From any interface-type to any value-type that implements the interface-type.

From the type System.Enum to any enum-type.

An unboxing operation consists of first checking that the object instance is a boxed value of the given value-type, and then copying the value out of the instance.

Referring to the imaginary boxing class described in the previous section, an unboxing conversion of an object box to a value-type T consists of executing the expression ((T_Box)box).value. Thus, the statements

object box = 123;

int i = (int)box;

conceptually correspond to

object box = new int_Box(123);

int i = ((int_Box)box).value;

For an unboxing conversion to a given value-type to succeed at run-time, the value of the source operand must be a reference to an object that was previously created by boxing a value of that value-type. If the source operand is null, a System.NullReferenceException is thrown. If the source operand is a reference to an incompatible object, a System.InvalidCastException is thrown

Routing

routing

(n.) In internetworking, the process of moving a packet of data from source to destination. Routing is usually performed by a dedicated device called a router.

Routing is a key feature of the Internet because it enables messages to pass from one computer to another and eventually reach the target machine.

Each intermediary computer performs routing by passing along the message to the next computer. Part of this process involves analyzing a routing table to determine the best path.

Routing is often confused with bridging, which performs a similar function. The principal difference between the two is that bridging occurs at a lower level and

is therefore more of a hardware function whereas routing occurs at a higher level where the software component is more important. And because routing occurs at

a higher level, it can perform more complex analysis to determine the optimal path for the packet.

delegates

A delegate in C# is similar to a function pointer in C or C++. Using a delegate allows the programmer to encapsulate a reference to a method inside a delegate object. The delegate object can then be passed to code which can call the referenced method, without having to know at compile time which method will be invoked. Unlike function pointers in C or C++, delegates are object-oriented, type-safe, and secure.

A delegate declaration defines a type that encapsulates a method with a particular set of arguments and return type. For static methods, a delegate object encapsulates the method to be called. For instance methods, a delegate object encapsulates both an instance and a method on the instance. If you have a delegate object and an appropriate set of arguments, you can invoke the delegate with the arguments.

An interesting and useful property of a delegate is that it does not know or care about the class of the object that it references. Any object will do; all that matters is that the method's argument types and return type match the delegate's. This makes delegates perfectly suited for "anonymous" invocation.

Note Delegates run under the caller's security permissions, not the declarer's permissions.

This tutorial includes two examples:

Example 1 shows how to declare, instantiate, and call a delegate.

Example 2 shows how to combine two delegates.

In addition, it discusses the following topics:

Delegates and Events

Delegates vs. Interfaces

Example 1

The following example illustrates declaring, instantiating, and using a delegate. The BookDB class encapsulates a bookstore database that maintains a database of books. It exposes a method ProcessPaperbackBooks, which finds all paperback books in the database and calls a delegate for each one. The delegate type used is called ProcessBookDelegate. The Test class uses this class to print out the titles and average price of the paperback books.

The use of delegates promotes good separation of functionality between the bookstore database and the client code. The client code has no knowledge of how the books are stored or how the bookstore code finds paperback books. The bookstore code has no knowledge of what processing is done on the paperback books after it finds them.

// bookstore.cs

using System;

// A set of classes for handling a bookstore:

namespace Bookstore

{

using System.Collections;

// Describes a book in the book list:

public struct Book

{

public string Title; // Title of the book.

public string Author; // Author of the book.

public decimal Price; // Price of the book.

public bool Paperback; // Is it paperback?

public Book(string title, string author, decimal price, bool paperBack)

{

Title = title;

Author = author;

Price = price;

Paperback = paperBack;

}

}

// Declare a delegate type for processing a book:

public delegate void ProcessBookDelegate(Book book);

// Maintains a book database.

public class BookDB

{

// List of all books in the database:

ArrayList list = new ArrayList();

// Add a book to the database:

public void AddBook(string title, string author, decimal price, bool paperBack)

{

list.Add(new Book(title, author, price, paperBack));

}

// Call a passed-in delegate on each paperback book to process it:

public void ProcessPaperbackBooks(ProcessBookDelegate processBook)

{

foreach (Book b in list)

{

if (b.Paperback)

// Calling the delegate:

processBook(b);

}

}

}

}

Declaring a delegate The following statement:

public delegate void ProcessBookDelegate(Book book);

declares a new delegate type. Each delegate type describes the number and types of the arguments, and the type of the return value of methods that it can encapsulate. Whenever a new set of argument types or return value type is needed, a new delegate type must be declared.

Instantiating a delegate Once a delegate type has been declared, a delegate object must be created and associated with a particular method. Like all other objects, a new delegate object is created with a new expression. When creating a delegate, however, the argument passed to the new expression is special — it is written like a method call, but without the arguments to the method.

The following statement:

bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle));

creates a new delegate object associated with the static method Test.PrintTitle. The following statement:

bookDB.ProcessPaperbackBooks(new

ProcessBookDelegate(totaller.AddBookToTotal));

creates a new delegate object associated with the nonstatic method AddBookToTotal on the object totaller. In both cases, this new delegate object is immediately passed to the ProcessPaperbackBooks method.

Note that once a delegate is created, the method it is associated with never changes — delegate objects are immutable.

Calling a delegate Once a delegate object is created, the delegate object is typically passed to other code that will call the delegate. A delegate object is called by using the name of the delegate object, followed by the parenthesized arguments to be passed to the delegate. An example of a delegate call is:

processBook(b);

A delegate can either be called synchronously, as in this example, or asynchronously by using BeginInvoke and EndInvoke methods.

Delegates and Events

Delegates are ideally suited for use as events — notifications from one component to "listeners" about changes in that component. For more information on the use of delegates for events, see the Events Tutorial.

Delegates vs. Interfaces

Delegates and interfaces are similar in that they enable the separation of specification and implementation. Multiple independent authors can produce implementations that are compatible with an interface specification. Similarly, a delegate specifies the signature of a method, and authors can write methods that are compatible with the delegate specification. When should you use interfaces, and when should you use delegates?

Delegates are useful when:

A single method is being called.

A class may want to have multiple implementations of the method specification.

It is desirable to allow using a static method to implement the specification.

An event-like design pattern is desired (for more information, see the Events Tutorial).

The caller has no need to know or obtain the object that the method is defined on.

The provider of the implementation wants to "hand out" the implementation of the specification to only a few select components.

Easy composition is desired.

Interfaces are useful when:

The specification defines a set of related methods that will be called.

A class typically implements the specification only once.

The caller of the interface wants to cast to or from the interface type to obtain other interfaces or classes.

0 comments;Click here for request info on this topic:

Post a Comment