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


Top Related