serialization implies a certain marshaling。 For example; it might mean an integer will be marshaled as an integer in another representation (in SQL Server; for example)。 Sometimes; however; you might want different representations in different streams。 In that case; you need to implement the marshaling of the data member yourself。 With most serialization platforms; it means imple menting a particular interface。 Following is an example that performs a custom serialization for a binary stream。 Imports System。Runtime。Serialization _ Class MyObject Implements ISerializable Private value As Integer Public Sub New() End Sub Public Sub New(ByVal info As SerializationInfo; ByVal context As StreamingContext) value = Integer。Parse(CStr(info。GetValue(〃value〃; GetType(String)))) End Sub Public Sub GetObjectData(ByVal info As SerializationInfo; ByVal context As StreamingContext) Implements ISerializable。GetObjectData info。AddValue(〃value〃; value。ToString()) End Sub End Class …………………………………………………………Page 300…………………………………………………………… 278 CH AP T E R 1 0 ■ L E A R N I N G A B OU T P E R S IS TE N CE In the example; the interface System。Runtime。Serialization。ISerializable is implemented。 This means when BinaryFormatter serializes or deserializes MyObject; BinaryFormatter will not manipulate the binary stream; but delegate the manipulation to MyObject。 With many serializa tion platforms; there is an explicit method; property; or flag to indicate whether MyObject is being written to the stream or read from the stream。 In the case of binary serialization; when an object is written to the stream; the GetObjectData() method is called; and when the object is being read from the stream; the constructor is called。 Serialization involves two directions; and a developer must implement both; in the same way。 In the example; the AddValue() method is called; indicating that the data member is written as a string; and when reading; the value data member must be read as a string。 ■Note One of the biggest challenges with serialization is that each and every serialization platform seems to have its own way of doing things。 Sometimes there will be mon methods and attributes; but other times they will not exist。 A universal approach to serialization won’t work。 You should avoid performing custom seri alization whenever possible。 Most serialization platforms are smart enough to know what to do with each data member。 So the best approach is to let the platform figure things out。 Declaring a Data Member As Nonserializable In the example of the Ticket type; all of the data members were serialized。 However; some times that is not desired。 Suppose an object that you want to serialize has a network connection or some other data value that will not make any sense if the object is serialized and then dese rialized some time later。 When the object is serialized; the network connection will also be serialized; which is not appropriate。 It is not appropriate to serialize a network connection because that object is transient and applies only to the context of the object instance。 To mark a data member as nonserializable; attributes are often used; as in the following example。 Class MyObject2 Private _networkIdentifier As Integer End Class In the example; _networkIdentifier will not be written or read from the data stream。 Separating Data Objects from Action Objects Another approach is to develop a number of data objects whose only role is to be used in seri alization and data referencing。 Such an approach is useful when using binary serialization; because you are then able to more effectively manage the version problem。 The following is an example of how such an architecture would be realized。 …………………………………………………………Page 301…………………………………………………………… CH A PT E R 1 0 ■ L E A R N I N G A B O U T P E R S IS T E N CE 279 Class MyObject2 End Class Class Doer Private _object As MyObject2 Private _networkIdentifier As Integer End Class The class Doer has no serialization attribute and will not be serialized; but it references MyObject2。 The network identifier data member has been moved from MyObject2 to Doer。 The result is that MyObject2 contains nothing that is transient。 pleting Custom Types When writing custom types; two methods are often implemented; especially where objects are to be pared a lot: Equals() and GetHashCode()。 These two methods are used by the library API when paring and manipulating instances in a list or a collection。 It just happened in this chapter’s example that a list of string types did the right thing。 If TextProcessor had used the Ticket type; then the Equals() method of the list used to find date duplicates would not have worked。 The default implementations of Equals() and GetHashCode() are not implemented properly。 This is not the API’s fault。 Rather; it is a recognition that Microsoft cannot know the structure of an object and what is considered as making a type unique。 Implementing GetHashCode() The MSDN documentation for Object。GetHashCode defines the GetHashCode() method as follows (http://msdn2。microsoft。/en…us/library/system。object。gethashc