tool development 06 - binary serialization, worker threads

40
Tool Development Chapter 06: Binary Serialization, Worker Threads Nick Prühs

Upload: nick-pruehs

Post on 19-May-2015

542 views

Category:

Technology


2 download

DESCRIPTION

Chapter 06 of the lecture Tool Development taught at SAE Institute Hamburg. Introduction to different approaches to binary serialization, as well as to worker threads in WPF.

TRANSCRIPT

Page 1: Tool Development 06 - Binary Serialization, Worker Threads

Tool DevelopmentChapter 06: Binary Serialization, Worker Threads

Nick Prühs

Page 2: Tool Development 06 - Binary Serialization, Worker Threads

5 Minute Review Session

• What is XML serialization?

• How is XML serialization controlled in .NET?

• What is XML Schema?

• What is the difference between simple and complex types in XML Schema?

• How do you define new simple types?

• How do you define new complex types?

• What is the major drawback of the INI file format?

• What is JSON?

• What is the main motivation behind YAML?

2 / 58

Page 3: Tool Development 06 - Binary Serialization, Worker Threads

Assignment Solution #5

DEMO

3 / 58

Page 4: Tool Development 06 - Binary Serialization, Worker Threads

Objectives

• To learn how to properly read and write binary files

• To understand how to use worker threads in order to create reactive UIs

4 / 58

Page 5: Tool Development 06 - Binary Serialization, Worker Threads

Binary Serialization

• Sometimes you’ll want to serialize data in a format other than plain text

• As we have learned, this can basically be achieved using the BinaryReader and BinaryWriter classes in .NET

5 / 58

Page 6: Tool Development 06 - Binary Serialization, Worker Threads

Writing Binary Data To Files

C#

6 / 58

// Collect information on the file to create.

FileInfo fileInfo = new FileInfo("newFile.dat");

// Create new file.

FileStream fileStream = fileInfo.Create();

// Create new binary writer.

BinaryWriter binaryWriter = new BinaryWriter(fileStream);

// Write data.

binaryWriter.Write(23);

binaryWriter.Write(true);

binaryWriter.Write(0.4f);

// Close file stream and release all resources.

binaryWriter.Close();

Page 7: Tool Development 06 - Binary Serialization, Worker Threads

Reading From Binary Files

C#

7 / 58

// Collect information on the file to read from.

FileInfo fileInfo = new FileInfo("newFile.dat");

// Open file for reading.

FileStream fileStream = fileInfo.OpenRead();

// Create new binary reader.

BinaryReader binaryReader = new BinaryReader(fileStream);

// Read data.

int i = binaryReader.ReadInt32();

bool b = binaryReader.ReadBoolean();

float f = binaryReader.ReadSingle();

// Close file stream and release all resources.

binaryReader.Close();

Page 8: Tool Development 06 - Binary Serialization, Worker Threads

Binary Serialization

• However, writing and reading binary data in the right order can be tedious and very error-prone.• In most cases you’ll need arbitrary data to be read and

written.

• Even worse, your type hierarchy will evolve during development.

• We’ll take a look at two more generic approaches that try to ensure mostly error-free binary serialization.

8 / 58

Page 9: Tool Development 06 - Binary Serialization, Worker Threads

Initial Situation

C#

9 / 58

public class Address

{

public string PostCode;

public string City;

}

public class OrderItem

{

public string Name;

public float Price;

}

public class Order

{

public OrderItem Item;

public Address ShipTo;

}

Page 10: Tool Development 06 - Binary Serialization, Worker Threads

Challenge

• In order to be able to automatically serialize and deserialize objects of type Order,• we need to recursively serialize and deserialize objects

of type Address and OrderItem,

• we must be able to read and write field values of primitive types (such as string or float),

• and we must do so in the right order!

10 / 58

Page 11: Tool Development 06 - Binary Serialization, Worker Threads

Binary Serializationvia Interfaces• All serializable classes implement a new interface

IBinarySerializable.

• Interface enforces methods for reading and writing binary data.• These methods can be called for all types that are

referenced by serialized types.

• Reading and writing data in the correct order relies on a correct implementation of the interface.

11 / 58

Page 12: Tool Development 06 - Binary Serialization, Worker Threads

InterfaceIBinarySerializable

C#

12 / 58

public interface IBinarySerializable

{

void WriteBinary(BinaryWriter writer);

void ReadBinary(BinaryReader reader);

}

Page 13: Tool Development 06 - Binary Serialization, Worker Threads

Interface Implementations

C#

13 / 58

public class Address : IBinarySerializable

{

public string PostCode;

public string City;

public void WriteBinary(BinaryWriter writer)

{

writer.Write(this.PostCode);

writer.Write(this.City);

}

public void ReadBinary(BinaryReader reader)

{

this.PostCode = reader.ReadString();

this.City = reader.ReadString();

}

}

Page 14: Tool Development 06 - Binary Serialization, Worker Threads

Interface Implementations

C#

14 / 58

public class OrderItem : IBinarySerializable

{

public string Name;

public float Price;

public void WriteBinary(BinaryWriter writer)

{

writer.Write(this.Name);

writer.Write(this.Price);

}

public void ReadBinary(BinaryReader reader)

{

this.Name = reader.ReadString();

this.Price = reader.ReadSingle();

}

}

Page 15: Tool Development 06 - Binary Serialization, Worker Threads

Interface Implementations

C#

15 / 58

public class Order : IBinarySerializable{

public OrderItem Item;public Address ShipTo;

public void WriteBinary(BinaryWriter writer){

this.Item.WriteBinary(writer);this.ShipTo.WriteBinary(writer);

}

public void ReadBinary(BinaryReader reader){

this.Item = new OrderItem();this.Item.ReadBinary(reader);

this.ShipTo = new Address();this.ShipTo.ReadBinary(reader);

}}

Page 16: Tool Development 06 - Binary Serialization, Worker Threads

Writing Binary Data

C#

16 / 58

// Collect information on the file to create.

FileInfo fileInfo = new FileInfo("newFile.dat");

// Create new file.

FileStream fileStream = fileInfo.Create();

// Create new binary writer.

BinaryWriter binaryWriter = new BinaryWriter(fileStream);

// Write data.

order.WriteBinary(binaryWriter);

// Close file stream and release all resources.

binaryWriter.Close();

Page 17: Tool Development 06 - Binary Serialization, Worker Threads

Reading Binary Data

C#

17 / 58

// Collect information on the file to read from.

FileInfo fileInfo = new FileInfo("newFile.dat");

// Open file for reading.

FileStream fileStream = fileInfo.OpenRead();

// Create new binary reader.

BinaryReader binaryReader = new BinaryReader(fileStream);

// Read data.

Order order = new Order();

order.ReadBinary(binaryReader);

// Close file stream and release all resources.

binaryReader.Close();

Page 18: Tool Development 06 - Binary Serialization, Worker Threads

Binary Serializationvia Interfaces - Evaluation• Delegating the task of serialization to serialized

classes increases code readability and makes debugging easier• However, every time a new type is introduced, you need

to implement the interface again.

• Reading and writing data in the correct order relies on a correct implementation of the interface.

• Strictly spoken, this approach violates the principle of separation of concerns.

18 / 58

Page 19: Tool Development 06 - Binary Serialization, Worker Threads

Binary Serializationvia Reflection• We create a new serialization class called BinarySerializer.

• Similar to XmlSerializer, this class provides Serialize and Deserialize methods that reflect the fields of a given type.

19 / 58

Page 20: Tool Development 06 - Binary Serialization, Worker Threads

Class BinarySerializer

C#

20 / 58

public void Serialize(BinaryWriter writer, object obj){

if (obj == null){

return;}

// Reflect object fields.Type type = obj.GetType();FieldInfo[] fields = type.GetFields

(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

// ...

Page 21: Tool Development 06 - Binary Serialization, Worker Threads

Class BinarySerializer

C#

21 / 58

// ...

foreach (FieldInfo field in fields){

// Check how the field value has to be serialized.object fieldValue = field.GetValue(obj);

if (field.FieldType == typeof(string)){

writer.Write((string)fieldValue);}else if (field.FieldType == typeof(float)){

writer.Write((float)fieldValue);}else if (field.FieldType == typeof(int)){

writer.Write((int)fieldValue);}

// ...}

}

Page 22: Tool Development 06 - Binary Serialization, Worker Threads

Class BinarySerializer

C#

22 / 58

foreach (FieldInfo field in fields){

// ...

else if (!field.FieldType.IsPrimitive){

// Recursively serialize referenced types.this.Serialize(writer, fieldValue);

}else{

throw new ArgumentException(string.Format("Unsupported type for binary serialization: {0}.Cannot serialize fields of type {1}.", type, field.FieldType), "obj");

}}

}

Page 23: Tool Development 06 - Binary Serialization, Worker Threads

Class BinarySerializer

C#

23 / 58

public object Deserialize(BinaryReader reader, Type type){

// Create object instance.object obj = Activator.CreateInstance(type);

// Reflect object fields.FieldInfo[] fields = type.GetFields

(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

// ...

Page 24: Tool Development 06 - Binary Serialization, Worker Threads

Class BinarySerializer

C#

24 / 58

// ...

foreach (FieldInfo field in fields){

object fieldValue;

// Check how the field value has to be deserialized.if (field.FieldType == typeof(string)){

fieldValue = reader.ReadString();}else if (field.FieldType == typeof(float)){

fieldValue = reader.ReadSingle();}else if (field.FieldType == typeof(int)){

fieldValue = reader.ReadInt32();}

// ...}

}

Page 25: Tool Development 06 - Binary Serialization, Worker Threads

Class BinarySerializer

C#

25 / 58

foreach (FieldInfo field in fields){

// ...

else if (!field.FieldType.IsPrimitive){

// Recursively deserialize referenced types.fieldValue = this.Deserialize(reader, field.FieldType);

}else{

throw new ArgumentException(string.Format("Unsupported type for binary deserialization: {0}.Cannot deserialize fields of type {1}.", type, field.FieldType), "type");

}

// Set field value.field.SetValue(obj, fieldValue);

}

return obj;}

Page 26: Tool Development 06 - Binary Serialization, Worker Threads

Writing Binary Data

C#

26 / 58

// Collect information on the file to create.

FileInfo fileInfo = new FileInfo("newFile.dat");

// Create new file.

FileStream fileStream = fileInfo.Create();

// Create new binary writer.

BinaryWriter binaryWriter = new BinaryWriter(fileStream);

// Write data.

BinarySerializer serializer = new BinarySerializer();

serializer.Serialize(binaryWriter, order);

// Close file stream and release all resources.

binaryWriter.Close();

Page 27: Tool Development 06 - Binary Serialization, Worker Threads

Reading Binary Data

C#

27 / 58

// Collect information on the file to read from.

FileInfo fileInfo = new FileInfo("newFile.dat");

// Open file for reading.

FileStream fileStream = fileInfo.OpenRead();

// Create new binary reader.

BinaryReader binaryReader = new BinaryReader(fileStream);

// Read data.

BinarySerializer serializer = new BinarySerializer();

Order order = (Order)serializer.Deserialize(binaryReader, typeof(Order));

// Close file stream and release all resources.

binaryReader.Close();

Page 28: Tool Development 06 - Binary Serialization, Worker Threads

Binary Serializationvia Reflection - Evaluation• Newly created types don’t need to implement any

interfaces.

• Special cases (enums, nullable types, generics) need to be considered only once.

• Serialization code is limited to very few lines, which in turn can be found in a single class.• However, serialization via reflection is significantly

slower than via interfaces.

• Reading and writing data in the correct order depends on the order the fields are declared in serialized types!

28 / 58

Page 29: Tool Development 06 - Binary Serialization, Worker Threads

Hint

Never stop improving your error handling while developing!

(e.g. missing default constructor, unexpected enum value, etc.)

30 / 78

Page 30: Tool Development 06 - Binary Serialization, Worker Threads

Background Workers

• Time-consuming operations like downloads and database transactions can cause your UI to seem as though it has stopped responding while they are running.

• BackgroundWorker class allows you to run an operation on a separate, dedicated thread.

31 / 78

Page 31: Tool Development 06 - Binary Serialization, Worker Threads

Background Workers 101

1. Create new BackgroundWorker object.

2. Add event handlers.1. Perform your time-consuming operation in DoWork.

2. Set WorkerReportsProgress to true and receive notifications of progress updates in ProgressChanged.

3. Receive a notification when the operation is completed in RunWorkerCompleted.

3. Call RunWorkerAsync on the worker object.

32 / 78

Page 32: Tool Development 06 - Binary Serialization, Worker Threads

Gotcha!

Don’t to manipulate any UI objects in your DoWork event handler!

33 / 78

Page 33: Tool Development 06 - Binary Serialization, Worker Threads

Background Worker and UI

34 / 78

Page 34: Tool Development 06 - Binary Serialization, Worker Threads

Reporting Progress

• Calling ReportProgress on the background worker object causes the ProgressChanged event handler to be called in the UI thread.

• In that event handler, the reported progress is available through the property ProgressChangedEventArgs.ProgressPercentage.

35 / 58

Page 35: Tool Development 06 - Binary Serialization, Worker Threads

Passing Parameters

• If your background operation requires a parameter, call RunWorkerAsync with your parameter.

• Inside the DoWork event handler, you can extract the parameter from the DoWorkEventArgs.Argument property.

36 / 58

Page 36: Tool Development 06 - Binary Serialization, Worker Threads

Returning Results

• If your background operation needs to return a result, set the DoWorkEventArgs.Result property in your DoWork event handler after your operation is finished.

• Inside the RunWorkerCompleted event handler of your UI thread, you can access the result from the RunWorkerCompletedEventArgs.Result property.

37 / 58

Page 37: Tool Development 06 - Binary Serialization, Worker Threads

Assignment #6

1. Status Bar

Add a StatusBar with a TextBlock and a ProgressBarto your MainWindow.

38 / 58

Page 38: Tool Development 06 - Binary Serialization, Worker Threads

Assignment #6

2. Worker Threads

1. Modify your application and make creating new mapshappen in a background worker thread.

2. Your status text and progress bar should reflect theprogress of the map creation.

39 / 58

Page 39: Tool Development 06 - Binary Serialization, Worker Threads

References

• MSDN. BackgroundWorker Class. http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker%28v=vs.110%29.aspx, May 2015.

40 / 58

Page 40: Tool Development 06 - Binary Serialization, Worker Threads

Thank you for your attention!

Contact

Mail

[email protected]

Blog

http://www.npruehs.de

Twitter

@npruehs

Github

https://github.com/npruehs

41 / 58