Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Visual CSharp 2005 Recipes (2006) [eng]

.pdf
Скачиваний:
48
Добавлен:
16.08.2013
Размер:
4.04 Mб
Скачать

58C H A P T E R 2 D ATA M A N I P U L AT I O N

//Remove the remaining items from the bag. string[] s = bag.RemoveAll();

//Wait to continue.

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

}

}

}

2-13. Store a Serializable Object to a File

Problem

You need to store a serializable object and its state to a file, and then deserialize it later.

Solution

Use a formatter to serialize the object and write it to a System.IO.FileStream object. When you need to retrieve the object, use the same type of formatter to read the serialized data from the file and deserialize the object. The .NET Framework class library includes the following formatter implementations for serializing objects to binary or SOAP format:

System.Runtime.Serialization.Formatters.Binary.BinaryFormatter

System.Runtime.Serialization.Formatters.Soap.SoapFormatter

How It Works

Using the BinaryFormatter and SoapFormatter classes, you can serialize an instance of any serializable type. (See recipe 13-1 for details on how to make a type serializable.) The BinaryFormatter class produces a binary data stream representing the object and its state. The SoapFormatter class produces a SOAP document.

Both the BinaryFormatter and SoapFormatter classes implement the interface System.Runtime. Serialization.IFormatter, which defines two methods: Serialize and Deserialize. The Serialize method takes a System.IO.Stream reference and a System.Object reference as arguments, serializes the Object, and writes it to the Stream. The Deserialize method takes a Stream reference as an argument, reads the serialized object data from the Stream, and returns an Object reference to a deserialized object. You must cast the returned Object reference to the correct type.

Caution To call the Serialize and Deserialize methods of the BinaryFormatter class, your code must be granted the SerializationFormatter element of the permission System.Security.Permissions. SecurityPermission. To call the Serialize and Deserialize methods of the SoapFormatter class, your code must be granted full trust, because the System.Runtime.Serialization.Formatters.Soap.dll assembly in which the SoapFormatter class is declared does not allow partially trusted callers. Refer to recipe 11-1 for more information about assemblies and partially trusted callers.

C H A P T E R 2 D ATA M A N I P U L AT I O N

59

The Code

The example shown here demonstrates the use of both BinaryFormatter and SoapFormatter to serialize a System.Collections.ArrayList object containing a list of people to a file. The ArrayList object is then deserialized from the files and the contents displayed to the console.

using System; using System.IO;

using System.Collections;

using System.Runtime.Serialization.Formatters.Soap; using System.Runtime.Serialization.Formatters.Binary;

namespace Apress.VisualCSharpRecipes.Chapter02

{

class Recipe02_13

{

//Serialize an ArrayList object to a binary file. private static void BinarySerialize(ArrayList list)

{

using (FileStream str = File.Create("people.bin"))

{

BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(str, list);

}

}

//Deserialize an ArrayList object from a binary file. private static ArrayList BinaryDeserialize()

{

ArrayList people = null;

using (FileStream str = File.OpenRead("people.bin"))

{

BinaryFormatter bf = new BinaryFormatter(); people = (ArrayList)bf.Deserialize(str);

}

return people;

}

//Serialize an ArrayList object to a SOAP file. private static void SoapSerialize(ArrayList list)

{

using (FileStream str = File.Create("people.soap"))

{

SoapFormatter sf = new SoapFormatter(); sf.Serialize(str, list);

}

}

//Deserialize an ArrayList object from a SOAP file. private static ArrayList SoapDeserialize()

{

ArrayList people = null;

using (FileStream str = File.OpenRead("people.soap"))

{

SoapFormatter sf = new SoapFormatter();

60 C H A P T E R 2 D ATA M A N I P U L AT I O N

people = (ArrayList)sf.Deserialize(str);

}

return people;

}

public static void Main()

{

//Create and configure the ArrayList to serialize ArrayList people = new ArrayList(); people.Add("Graeme");

people.Add("Lin");

people.Add("Andy");

//Serialize the list to a file in both binary and SOAP form. BinarySerialize(people);

SoapSerialize(people);

//Rebuild the lists of people from the binary and SOAP

//serializations and display them to the console.

ArrayList binaryPeople = BinaryDeserialize();

ArrayList soapPeople = SoapDeserialize();

Console.WriteLine("Binary people:"); foreach (string s in binaryPeople)

{

Console.WriteLine("\t" + s);

}

Console.WriteLine("\nSOAP people:"); foreach (string s in soapPeople)

{

Console.WriteLine("\t" + s);

}

// Wait to continue.

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

}

}

}

Usage

To illustrate the different results achieved using the BinaryFormatter and SoapFormatter classes, Figure 2-1 shows the contents of the people.bin file generated using the BinaryFormatter class, and Figure 2-2 shows the contents of the people.soap file generated using the SoapFormatter class.

C H A P T E R 2 D ATA M A N I P U L AT I O N

61

Figure 2-1. Contents of the people.bin file

Figure 2-2. Contents of the people.soap file

2-14. Read User Input from the Console

Problem

You want to read user input from the Windows console, either a line or character at a time.

Solution

Use the Read or ReadLine method of the System.Console class to read input when the user presses Enter. To read input without requiring the user to press Enter, use the Console.ReadKey method.

How It Works

The simplest way to read input from the console is to use the static Read or ReadLine methods of the Console class. These methods will both cause your application to block, waiting for the user to enter input and press Enter. In both instances, the user will see the input characters in the console. Once the user presses Enter, the Read method will return an int value representing the next character of input data, or –1 if no more data is available. The ReadLine method will return a string containing all the data entered, or an empty string if no data was entered.

62 C H A P T E R 2 D ATA M A N I P U L AT I O N

.NET Framework 2.0 adds the ReadKey method to the Console class, which provides a way to read input from the console without waiting for the user to press Enter. The ReadKey method waits for the user to press a key and returns a System.ConsoleKeyInfo object to the caller. By passing true as an argument to an overload of the ReadKey method, you can also prevent the key pressed by the user from being echoed to the console.

The returned ConsoleKeyInfo object contains details about the key pressed. The details are accessible through the properties of the ConsoleKeyInfo class summarized in Table 2-5.

Table 2-5. Properties of the ConsoleKeyInfo Class

Property

Description

Key

Gets a value of the System.ConsoleKey enumeration representing the key

 

pressed. The ConsoleKey enumeration contains values that represent all of the

 

keys usually found on a keyboard. These include all the character and function

 

keys; navigation and editing keys like Home, Insert, and Delete; and more modern

 

specialized keys like the Windows key, media player control keys, browser

 

activation keys, and browser navigation keys.

KeyChar

Gets a char value containing the Unicode character representation of the key

 

pressed.

Modifiers

Gets a bitwise combination of values from the System.ConsoleModifiers

 

enumeration that identifies one or more modifier keys pressed simultaneously

 

with the console key. The members of the ConsoleModifiers enumeration are

 

Alt, Control, and Shift.

 

 

The KeyAvailable method of the Console class returns a bool value indicating whether input is available in the input buffer without blocking your code.

The Code

The following example reads input from the console one character at a time using the ReadKey method. If the user presses F1, the program toggles in and out of “secret” mode, where input is masked by asterisks. When the user presses Escape, the console is cleared and the input the user has entered is displayed. If the user presses Alt-X or Alt-x, the example terminates.

using System;

using System.Collections.Generic;

namespace Apress.VisualCSharpRecipes.Chapter02

{

class Recipe02_14

{

public static void Main()

{

//Local variable to hold the key entered by the user. ConsoleKeyInfo key;

//Control whether character or asterisk is displayed. bool secret = false;

//Character List for the user data entered. List<char> input = new List<char>();

C H A P T E R 2 D ATA M A N I P U L AT I O N

63

string msg = "Enter characters and press Escape to see input." + "\nPress F1 to enter/exit Secret mode and Alt-X to exit.";

Console.WriteLine(msg);

// Process input until the user enters "Alt-X" or "Alt-x". do

{

//Read a key from the console. Intercept the key so that it is not

//displayed to the console. What is displayed is determined later

//depending on whether the program is in secret mode.

key = Console.ReadKey(true);

// Switch secret mode on and off. if (key.Key == ConsoleKey.F1)

{

if (secret)

{

// Switch secret mode off. secret = false;

}

else

{

// Switch secret mode on. secret = true;

}

}

// Handle Backspace.

if (key.Key == ConsoleKey.Backspace)

{

if (input.Count > 0)

{

// Backspace pressed, remove the last character. input.RemoveAt(input.Count - 1);

Console.Write(key.KeyChar); Console.Write(" "); Console.Write(key.KeyChar);

}

}

// Handle Escape.

else if (key.Key == ConsoleKey.Escape)

{

Console.Clear(); Console.WriteLine("Input: {0}\n\n",

new String(input.ToArray())); Console.WriteLine(msg); input.Clear();

}

// Handle character input.

else if (key.Key >= ConsoleKey.A && key.Key <= ConsoleKey.Z)

{

input.Add(key.KeyChar);

64 C H A P T E R 2 D ATA M A N I P U L AT I O N

if (secret)

{

Console.Write("*");

}

else

{

Console.Write(key.KeyChar);

}

}

}while (key.Key != ConsoleKey.X

||key.Modifiers != ConsoleModifiers.Alt);

// Wait to continue.

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

}

}

}

C H A P T E R 3

■ ■ ■

Application Domains, Reflection,

and Metadata

The power and flexibility of the Microsoft .NET Framework is enhanced by the ability to inspect and manipulate types and metadata at runtime. The recipes in this chapter describe how to use application domains, reflection, and metadata. Specifically, the recipes in this chapter describe how to do the following:

Create application domains into which you can load assemblies that are isolated from the rest of your application (recipe 3-1)

Create types that have the capability to cross application domain boundaries (recipe 3-2) and types that are guaranteed to be unable to cross application domain boundaries (recipe 3-4)

Control the loading of assemblies and the instantiation of types in local and remote application domains (recipes 3-3, 3-5, 3-6, and 3-7)

Pass simple configuration data between application domains (recipe 3-8)

Unload application domains, which provides the only means through which you can unload assemblies at runtime (recipe 3-9)

Inspect and test the type of an object using a variety of mechanisms built into the C# language and capabilities provided by the objects themselves (recipes 3-10 and 3-11)

Dynamically instantiate an object and execute its methods at runtime using reflection (recipe 3-12)

Create custom attributes (recipe 3-13), allowing you to associate metadata with your program elements and inspect the value of those custom attributes at runtime (recipe 3-14)

Note An excellent reference for detailed information on all aspects of application domains and loading assemblies is Customizing the Microsoft .NET Framework Common Language Runtime by Steven Pratschner (Microsoft Press, 2005).

65

66 C H A P T E R 3 A P P L I C AT I O N D O M A I N S, R E F L E C T I O N, A N D M E TA D ATA

3-1. Create an Application Domain

Problem

You need to create a new application domain.

Solution

Use the static method CreateDomain of the System.AppDomain class.

How It Works

The simplest overload of the CreateDomain method takes a single string argument specifying a humanreadable name (friendly name) for the new application domain. Other overloads allow you to specify evidence and configuration settings for the new application domain. You specify evidence using a System.Security.Policy.Evidence object, and you specify configuration settings using

a System.AppDomainSetup object.

The AppDomainSetup class is a container of configuration information for an application domain. Table 3-1 lists some of the properties of the AppDomainSetup class that you will use most often when creating application domains. These properties are accessible after creation through members of the AppDomain object. Some have different names, and some are modifiable at runtime; refer to the

.NET Framework’s software development kit (SDK) documentation on the AppDomain class for a comprehensive discussion.

Table 3-1. Commonly Used AppDomainSetup Properties

Property

Description

ApplicationBase

The directory where the CLR will look during probing to resolve private

 

assemblies. (Recipe 3-5 discusses probing.) Effectively, ApplicationBase

 

is the root directory for the executing application. By default, this is the

 

directory containing the assembly. This is readable after creation using

 

the AppDomain.BaseDirectory property.

ConfigurationFile

The name of the configuration file used by code loaded into the

 

application domain. This is readable after creation using the

 

AppDomain.GetData method with the key APP_CONFIG_FILE. By

 

default, the configuration file is stored in the same folder as the

 

application .exe file, but if you set ApplicationBase, it will be in that

 

same folder.

DisallowPublisherPolicy

Controls whether the publisher policy section of the application

 

configuration file is taken into consideration when determining which

 

version of a strong-named assembly to bind to. Recipe 3-5 discusses

 

publisher policy.

PrivateBinPath

A semicolon-separated list of directories that the runtime uses when

 

probing for private assemblies. These directories are relative to the

 

directory specified in ApplicationBase. This is readable after application

 

domain creation using the AppDomain.RelativeSearchPath property.

 

 

The Code

The following code demonstrates the creation and initial configuration of an application domain:

C H A P T E R 3 A P P L I C AT I O N D O M A I N S, R E F L E C T I O N, A N D M E TA D ATA

67

using System;

namespace Apress.VisualCSharpRecipes.Chapter03

{

class Recipe03_01

{

public static void Main()

{

//Instantiate an AppDomainSetup object. AppDomainSetup setupInfo = new AppDomainSetup();

//Configure the application domain setup information. setupInfo.ApplicationBase = @"C:\MyRootDirectory"; setupInfo.ConfigurationFile = "MyApp.config"; setupInfo.PrivateBinPath = "bin;plugins;external";

//Create a new application domain passing null as the evidence

//argument. Remember to save a reference to the new AppDomain as

//this cannot be retrieved any other way.

AppDomain newDomain =

AppDomain.CreateDomain("My New AppDomain",null, setupInfo);

// Wait to continue.

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

}

}

}

Note You must maintain a reference to the AppDomain object when you create it because no mechanism exists to enumerate existing application domains from within managed code.

3-2. Create Types That Can Be Passed Across Application Domain Boundaries

Problem

You need to pass objects across application domain boundaries as arguments or return values.

Solution

Use marshal-by-value or marshal-by-reference objects.

How It Works

The .NET Remoting system (discussed in Chapter 10) makes passing objects across application domain boundaries straightforward. However, to those unfamiliar with .NET Remoting, the results can be very different from those expected. In fact, the most confusing aspect of using multiple

Соседние файлы в предмете Программирование на C++