Lập trình ứng dụng nâng cao (phần 12) pot

50 242 0
Lập trình ứng dụng nâng cao (phần 12) pot

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

532 | Chapter 22: Streams You are now ready to call the formatter’s Serialize( ) method, passing in the stream and the object to serialize. Because this is done in a method of SumOf, you can pass in the this object, which points to the current object: binaryFormatter.Serialize(fileStream,this); This serializes the SumOf object to disk. Deserializing the object To reconstitute the object, open the file, and ask a binary formatter to DeSerialize it: public static SumOf DeSerialize( ){ FileStream fileStream = new FileStream("DoSum.out",FileMode.Open); BinaryFormatter binaryFormatter = new BinaryFormatter( ); SumOf retVal = (SumOf) binaryFormatter.Deserialize(fileStream); fileStream.Close( ); return retVal;} To make sure all this works, first, instantiate a new object of type SumOf and tell it to serialize itself. Then, create a new instance of type SumOf by calling the static deserial- izer and asking it to display its values: public static void Main( ) { Console.WriteLine("Creating first one with new "); SumOf app = new SumOf(1,10); Console.WriteLine( "Creating second one with deserialize "); SumOf newInstance = SumOf.DeSerialize( ); newInstance.DisplaySums( ); } Example 22-14 provides the complete source code to illustrate serialization and deserialization. Example 22-14. Serializing and deserializing an object using System; using System.Collections.Generic; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Text; namespace SerializingDeserializingAnObject { [Serializable] class SumOf { Serialization | 533 private int startNumber = 1; private int endNumber; private int[] theSums; public static void Main( ) { Console.WriteLine("Creating first one with new "); SumOf app = new SumOf(1, 10); Console.WriteLine("Creating second one with deserialize "); SumOf newInstance = SumOf.DeSerialize( ); newInstance.DisplaySums( ); } public SumOf(int start, int end) { startNumber = start; endNumber = end; ComputeSums( ); DisplaySums( ); Serialize( ); } private void ComputeSums( ) { int count = endNumber - startNumber + 1; theSums = new int[count]; theSums[0] = startNumber; for (int i = 1, j = startNumber + 1; i < count; i++, j++) { theSums[i] = j + theSums[i - 1]; } } private void DisplaySums( ) { foreach (int i in theSums) { Console.WriteLine("{0}, ", i); } } private void Serialize( ) { Console.Write("Serializing "); // create a file stream to write the file FileStream fileStream = new FileStream("DoSum.out", FileMode.Create); // use the CLR binary formatter BinaryFormatter binaryFormatter = new BinaryFormatter( ); Example 22-14. Serializing and deserializing an object (continued) 534 | Chapter 22: Streams The output shows that the object was created, displayed, and then serialized. The object was then deserialized and output again, with no loss of data. // serialize to disk binaryFormatter.Serialize(fileStream, this); Console.WriteLine(" completed"); fileStream.Close( ); } public static SumOf DeSerialize( ) { FileStream fileStream = new FileStream("DoSum.out", FileMode.Open); BinaryFormatter binaryFormatter = new BinaryFormatter( ); SumOf retVal = (SumOf)binaryFormatter.Deserialize(fileStream); fileStream.Close( ); return retVal; } } } Output: Creating first one with new 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, Serializing completed Creating second one with deserialize 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, Example 22-14. Serializing and deserializing an object (continued) Serialization | 535 Handling Transient Data In some ways, the approach to serialization demonstrated in Example 22-14 is very wasteful. Because you can compute the contents of the array given its starting and ending numbers, there really is no reason to store its elements to disk. Although the operation might be inexpensive with a small array, it could become costly with a very large one. You can tell the serializer not to serialize some data by marking it with the [NonSerialized] attribute: [NonSerialized] private int[] theSums; If you don’t serialize the array, however, the object you create will not be correct when you deserialize it. The array will be empty. Remember, when you deserialize the object, you simply read it up from its serialized form; no methods are run. To fix the object before you return it to the caller, implement the IDeserializationCallback interface: [Serializable] class SumOf : IDeserializationCallback Also, implement the one method of this interface: OnDeserialization( ). The CLR promises that if you implement this interface, your class’ OnDeserialization( ) method will be called when the entire object graph has been deserialized. This is just what you want: the CLR will reconstitute what you’ve serialized, and then you have the opportunity to fix up the parts that were not serialized. This implementation can be very simple. Just ask the object to recompute the series: public virtual void OnDeserialization (Object sender) { ComputeSums( ); } This is a classic space/time trade-off; by not serializing the array, you may make dese- rialization somewhat slower (because you must take the time to recompute the array), and you make the file somewhat smaller. To see whether not serializing the array had any effect, I ran the program with the digits 1–5,000. Before setting [NonSerialized] on the array, the serialized file was 20 KB. After setting [NonSerialized], the file was 1 KB. Not bad. Example 22-15 shows the source code using the digits 1–5 as input (to simplify the output). Example 22-15. Working with a nonserialized object using System; using System.Collections.Generic; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Text; 536 | Chapter 22: Streams namespace WorkingWithNonSerializedObject { [Serializable] class SumOf : IDeserializationCallback { private int startNumber = 1; private int endNumber; [NonSerialized] private int[] theSums; public static void Main( ) { Console.WriteLine("Creating first one with new "); SumOf app = new SumOf(1, 5); Console.WriteLine("Creating second one with deserialize "); SumOf newInstance = SumOf.DeSerialize( ); newInstance.DisplaySums( ); } public SumOf(int start, int end) { startNumber = start; endNumber = end; ComputeSums( ); DisplaySums( ); Serialize( ); } private void ComputeSums( ) { int count = endNumber - startNumber + 1; theSums = new int[count]; theSums[0] = startNumber; for (int i = 1, j = startNumber + 1; i < count; i++, j++) { theSums[i] = j + theSums[i - 1]; } } private void DisplaySums( ) { foreach (int i in theSums) { Console.WriteLine("{0}, ", i); } } private void Serialize( ) { Console.Write("Serializing "); // create a file stream to write the file Example 22-15. Working with a nonserialized object (continued) Serialization | 537 You can see in the output that the data was successfully serialized to disk and then reconstituted by deserialization. The trade-off of disk storage space versus time doesn’t make a lot of sense with five values, but it makes a great deal of sense with five million values. FileStream fileStream = new FileStream("DoSum.out", FileMode.Create); // use the CLR binary formatter BinaryFormatter binaryFormatter = new BinaryFormatter( ); // serialize to disk binaryFormatter.Serialize(fileStream, this); Console.WriteLine(" completed"); fileStream.Close( ); } public static SumOf DeSerialize( ) { FileStream fileStream = new FileStream("DoSum.out", FileMode.Open); BinaryFormatter binaryFormatter = new BinaryFormatter( ); SumOf retVal = (SumOf)binaryFormatter.Deserialize(fileStream); fileStream.Close( ); return retVal; } // fix up the nonserialized data public virtual void OnDeserialization(Object sender) { ComputeSums( ); } } } Output: Creating first one with new 1, 3, 6, 10, 15, Serializing completed Creating second one with deserialize 1, 3, 6, 10, 15, Example 22-15. Working with a nonserialized object (continued) 538 | Chapter 22: Streams So far, you’ve streamed your data to disk for storage and across the network for easy communication with distant programs. There is one other time you might create a stream: to store permanent configuration and status data on a per-user basis. For this purpose, the .NET Framework offers isolated storage. Isolated Storage The .NET CLR provides isolated storage to allow the application developer to store data on a per-user basis. Isolated storage provides much of the functionality of tradi- tional Windows .ini files, or the more recent HKEY_CURRENT_USER key in the Windows Registry. Applications save data to a unique data compartment associated with the applica- tion. The CLR implements the data compartment with a data store, which is typically a directory on the filesystem. Administrators are free to limit how much isolated storage individual applications can use. They can also use security so that less-trusted code can’t call more highly trusted code to write to isolated storage. What is important about isolated storage is that the CLR provides a standard place to store your application’s data, but it doesn’t impose (or support) any particular layout or syntax for that data. In short, you can store anything you like in isolated storage. Typically, you will store text, often in the form of name-value pairs. Isolated storage is a good mechanism for saving user configuration information such as login name, the position of various windows and widgets, and other application-specific, user- specific information. The data is stored in a separate file for each user, but the files can be isolated even further by distinguishing among different aspects of the identity of the code (by assembly or by originating application domain). Using isolated storage is fairly straightforward. To write to isolated storage, create an instance of an IsolatedStorageFileStream, which you initialize with a filename and a file mode (create, append, etc.): IsolatedStorageFileStream configFile = new IsolatedStorageFileStream ("Tester.cfg",FileMode.Create); Now, create a StreamWriter on that file: StreamWriter writer = new StreamWriter(configFile); Then, write to that stream as you would to any other. Example 22-16 illustrates. Isolated Storage | 539 After running this code, search your hard disk for Tester.cfg. On my machine, this file is found in: C:\Documents and Settings\Jesse\Local Settings\Application Data\ IsolatedStorage\mipjwcsz.iir\2hzvpjcc.p0y\StrongName. mwoxzllzqpx3u0taclp1dti11kpddwyo\Url.a2f4v2g3ytucslmvlpt2wmdxhrhqg1pz\ Files Example 22-16. Writing to isolated storage using System; using System.Collections.Generic; using System.IO; using System.IO.IsolatedStorage; using System.Text; namespace WritingToIsolatedStorage { public class Tester { public static void Main( ) { Tester app = new Tester( ); app.Run( ); } private void Run( ) { // create the configuration file stream IsolatedStorageFileStream configFile = new IsolatedStorageFileStream ("Tester.cfg", FileMode.Create); // create a writer to write to the stream StreamWriter writer = new StreamWriter(configFile); // write some data to the config. file String output; System.DateTime currentTime = System.DateTime.Now; output = "Last access: " + currentTime.ToString( ); writer.WriteLine(output); output = "Last position = 27,35"; writer.WriteLine(output); // flush the buffer and clean up writer.Close( ); configFile.Close( ); } } } 540 | Chapter 22: Streams You can read this file with Notepad if what you’ve written is just text: Last access: 8/26/2007 10:00:57 AM Last position = 27,35 Or, you can access this data programmatically. To do so, reopen the file: IsolatedStorageFileStream configFile = new IsolatedStorageFileStream ("Tester.cfg",FileMode.Open); Create a StreamReader object: StreamReader reader = new StreamReader(configFile); Use the standard stream idiom to read through the file: string theEntry; do { theEntry = reader.ReadLine( ); Console.WriteLine(theEntry); } while (theEntry != null); Console.WriteLine(theEntry); Isolated storage is scoped by assembly (so if you shut down your program and start it later, you can read the configuration file you created, but you can’t read the configu- ration of any other assembly). Example 22-17 provides the method needed to read the file. Replace the Run( ) method in the previous example, recompile it, and run it (but don’t change its name, or it won’t be able to access the isolated storage you cre- ated previously). Example 22-17. Reading from isolated storage private void Run( ) { // open the configuration file stream IsolatedStorageFileStream configFile = new IsolatedStorageFileStream ("Tester.cfg", FileMode.Open); // create a standard stream reader StreamReader reader = new StreamReader(configFile); // read through the file and display string theEntry; do { theEntry = reader.ReadLine( ); Console.WriteLine(theEntry); } while (theEntry != null); Isolated Storage | 541 reader.Close( ); configFile.Close( ); } } Output: Last access: 8/26/2007 11:19:51 PM Last position = 27,35 Example 22-17. Reading from isolated storage (continued)

Ngày đăng: 07/07/2014, 05:20

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan