Change Encoding key to workaround collision with Encoding parameter names used by...
[mono.git] / mcs / class / referencesource / System.Data.Linq / Types.cs
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using System.ComponentModel;
5 using System.Linq.Expressions;
6 using System.IO;
7 using System.Linq;
8 using System.Reflection;
9 using System.Text;
10 using System.Diagnostics;
11 using System.Runtime.Serialization;
12 using System.Diagnostics.CodeAnalysis;
13
14 namespace System.Data.Linq {
15     internal static class SourceState<T> {
16         internal static readonly IEnumerable<T> Loaded = (IEnumerable<T>)new T[] { };
17         internal static readonly IEnumerable<T> Assigned = (IEnumerable<T>)new T[] { };
18     }
19
20     [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification = "[....]: Types are never compared to each other.  When comparisons happen it is against the entities that are represented by these constructs.")]
21     public struct Link<T> {
22         T underlyingValue;
23         IEnumerable<T> source;
24
25         public Link(T value) {
26             this.underlyingValue = value;
27             this.source = null;
28         }
29
30         public Link(IEnumerable<T> source) {
31             this.source = source;
32             this.underlyingValue = default(T);
33         }
34
35         public Link(Link<T> link) {
36             this.underlyingValue = link.underlyingValue;
37             this.source = link.source;
38         }
39
40         public bool HasValue {
41             get { return this.source == null || this.HasLoadedValue || this.HasAssignedValue; }
42         }
43
44         public bool HasLoadedOrAssignedValue {
45             get { return this.HasLoadedValue || this.HasAssignedValue; }
46         }
47
48         internal bool HasLoadedValue {
49             get { return this.source == SourceState<T>.Loaded; }
50         }
51
52         internal bool HasAssignedValue {
53             get { return this.source == SourceState<T>.Assigned; }
54         }
55
56         internal T UnderlyingValue {
57             get { return this.underlyingValue; }
58         }
59
60         internal IEnumerable<T> Source {
61             get { return this.source; }
62         }
63
64         internal bool HasSource {
65             get { return this.source != null && !this.HasAssignedValue && !this.HasLoadedValue; }
66         }
67
68         public T Value {
69             get {
70                 if (this.HasSource) {
71                     this.underlyingValue = Enumerable.SingleOrDefault(this.source);
72                     this.source = SourceState<T>.Loaded;
73                 }
74                 return this.underlyingValue;
75             }
76             set {
77                 this.underlyingValue = value;
78                 this.source = SourceState<T>.Assigned;
79             }
80         }
81     }
82
83     [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification="[....]: Types are never compared to each other.  When comparisons happen it is against the entities that are represented by these constructs.")]
84     public struct EntityRef<TEntity>
85         where TEntity : class {
86         IEnumerable<TEntity> source;
87         TEntity entity;
88
89         public EntityRef(TEntity entity) {
90             this.entity = entity;
91             this.source = SourceState<TEntity>.Assigned;
92         }
93
94         public EntityRef(IEnumerable<TEntity> source) {
95             this.source = source;
96             this.entity = default(TEntity);
97         }
98
99         public EntityRef(EntityRef<TEntity> entityRef) {
100             this.source = entityRef.source;
101             this.entity = entityRef.entity;
102         }
103
104         public TEntity Entity {
105             get {
106                 if (this.HasSource) {
107                     
108                     IEnumerable<TEntity> src = this.source;
109                     this.entity = Enumerable.SingleOrDefault(src);
110                     this.source = SourceState<TEntity>.Loaded;
111                 }
112                 return this.entity;
113             }
114             set {
115                 this.entity = value;
116                 this.source = SourceState<TEntity>.Assigned;
117             }
118         }
119
120         public bool HasLoadedOrAssignedValue {
121             get { return this.HasLoadedValue || this.HasAssignedValue; }
122         }
123
124         internal bool HasValue {
125             get { return this.source == null || this.HasLoadedValue || this.HasAssignedValue; }
126         }
127
128         internal bool HasLoadedValue {
129             get { return this.source == SourceState<TEntity>.Loaded; }
130         }
131
132         internal bool HasAssignedValue {
133             get { return this.source == SourceState<TEntity>.Assigned; }
134         }
135
136         internal bool HasSource {
137             get { return this.source != null && !this.HasLoadedValue && !this.HasAssignedValue; }
138         }
139
140         internal IEnumerable<TEntity> Source {
141             get { return this.source; }
142         }
143
144         internal TEntity UnderlyingValue {
145             get { return this.entity; }
146         }
147     }
148
149     [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", Justification="[....]: Naming chosen to represent a different concept from a collection because it is delayed loaded.")]
150     public sealed class EntitySet<TEntity> : IList, IList<TEntity>, IListSource
151         where TEntity : class {
152         IEnumerable<TEntity> source;
153         ItemList<TEntity> entities;
154         ItemList<TEntity> removedEntities;
155         Action<TEntity> onAdd;
156         Action<TEntity> onRemove;
157         TEntity onAddEntity;
158         TEntity onRemoveEntity;
159         int version;
160         private ListChangedEventHandler onListChanged;
161         private bool isModified;
162         private bool isLoaded;
163         bool listChanged;
164
165         public EntitySet() {
166         }
167
168         public EntitySet(Action<TEntity> onAdd, Action<TEntity> onRemove) {
169             this.onAdd = onAdd;
170             this.onRemove = onRemove;
171         }
172
173         internal EntitySet(EntitySet<TEntity> es, bool copyNotifications) {
174             this.source = es.source;
175             foreach (TEntity e in es.entities) entities.Add(e);
176             foreach (TEntity e in es.removedEntities) removedEntities.Add(e);
177             this.version = es.version;
178             if (copyNotifications) {
179                 this.onAdd = es.onAdd;
180                 this.onRemove = es.onRemove;
181             }
182         }
183
184         public int Count {
185             get {
186                 Load();
187                 return entities.Count;
188             }
189         }
190
191         public TEntity this[int index] {
192             get {
193                 Load();
194                 if (index < 0 || index >= entities.Count)
195                     throw Error.ArgumentOutOfRange("index");
196                 return entities[index];
197             }
198             set {
199                 Load();
200                 if (index < 0 || index >= entities.Count)
201                     throw Error.ArgumentOutOfRange("index");
202                 if (value == null || IndexOf(value) >= 0)
203                     throw Error.ArgumentOutOfRange("value");
204                 CheckModify();
205                 TEntity old = entities[index];
206                 OnRemove(old);
207                 OnListChanged(ListChangedType.ItemDeleted, index);
208
209                 OnAdd(value);
210                 entities[index] = value;
211                 OnModified();
212                 OnListChanged(ListChangedType.ItemAdded, index);
213             }
214         }
215
216         [SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Justification = "[....]: Naming the parameter entity makes it more discoverable because it is clear what type of data should be added to this collection.")]
217         public void Add(TEntity entity) {
218             if (entity == null) {
219                 throw Error.ArgumentNull("entity");
220             }
221             if (entity != onAddEntity) {
222                 CheckModify();
223                 if (!entities.Contains(entity)) {
224                     OnAdd(entity);
225                     if (this.HasSource) removedEntities.Remove(entity);
226                     entities.Add(entity);
227                     OnListChanged(ListChangedType.ItemAdded, entities.IndexOf(entity));
228                 }
229                 OnModified();
230             }
231         }
232
233         public void AddRange(IEnumerable<TEntity> collection) {
234             if (collection == null)
235                 throw Error.ArgumentNull("collection");
236             CheckModify();
237             // convert to List in case adding elements here removes them from the 'collection' (ie entityset to entityset assignment)
238             collection = collection.ToList();
239             foreach (TEntity e in collection) {
240                 if (!entities.Contains(e)) {
241                     OnAdd(e);
242                     if (this.HasSource) removedEntities.Remove(e);
243                     entities.Add(e);
244                     OnListChanged(ListChangedType.ItemAdded, entities.IndexOf(e));
245                 }
246             }
247             OnModified();
248         }
249
250         public void Assign(IEnumerable<TEntity> entitySource) {
251             // No-op if assigning the same object to itself
252             if (Object.ReferenceEquals(this, entitySource)) {
253                 return;
254             }
255
256             Clear();
257             if (entitySource != null)
258                 AddRange(entitySource);
259
260             // When an entity set is assigned, it is considered loaded.
261             // Since with defer loading enabled, a load is triggered
262             // anyways, this is only necessary in cases where defer loading
263             // is disabled.  In such cases, the materializer assigns a 
264             // prefetched collection and we want IsLoaded to be true.
265             this.isLoaded = true;
266         }
267
268         public void Clear() {
269             Load();
270             CheckModify();
271             if (entities.Items != null) {
272                 List<TEntity> removeList = new List<TEntity>(entities.Items);
273                 foreach (TEntity e in removeList) {
274                     Remove(e);
275                 }
276             }
277             entities = default(ItemList<TEntity>);
278             OnModified();
279             OnListChanged(ListChangedType.Reset, 0);
280         }
281
282         [SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Justification = "[....]: Naming the parameter entity makes it more discoverable because it is clear what type of data should be added to this collection.")]
283         public bool Contains(TEntity entity) {
284             return IndexOf(entity) >= 0;
285         }
286
287         public void CopyTo(TEntity[] array, int arrayIndex) {
288             Load();
289             if (entities.Count > 0) Array.Copy(entities.Items, 0, array, arrayIndex, entities.Count);
290         }
291
292         public IEnumerator<TEntity> GetEnumerator() {
293             Load();
294             return new Enumerator(this);
295         }
296
297         internal IEnumerable<TEntity> GetUnderlyingValues() {
298             return new UnderlyingValues(this);
299         }
300
301         class UnderlyingValues : IEnumerable<TEntity> {
302             EntitySet<TEntity> entitySet;
303             internal UnderlyingValues(EntitySet<TEntity> entitySet) {
304                 this.entitySet = entitySet;
305             }
306             public IEnumerator<TEntity> GetEnumerator() {
307                 return new Enumerator(this.entitySet);
308             }
309             IEnumerator IEnumerable.GetEnumerator() {
310                 return this.GetEnumerator();
311             }
312         }
313
314         [SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Justification = "[....]: Naming the parameter entity makes it more discoverable because it is clear what type of data should be added to this collection.")]
315         public int IndexOf(TEntity entity) {
316             Load();
317             return entities.IndexOf(entity);
318         }
319
320         [SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Justification = "[....]: Naming the parameter entity makes it more discoverable because it is clear what type of data should be added to this collection.")]
321         public void Insert(int index, TEntity entity) {
322             Load();
323             if (index < 0 || index > Count)
324                 throw Error.ArgumentOutOfRange("index");
325             if (entity == null || IndexOf(entity) >= 0)
326                 throw Error.ArgumentOutOfRange("entity");
327             CheckModify();
328             entities.Insert(index, entity);
329             OnListChanged(ListChangedType.ItemAdded, index);
330
331             OnAdd(entity);
332         }
333
334         /// <summary>
335         /// Returns true if this entity set has a deferred query
336         /// that hasn't been executed yet.
337         /// </summary>
338         public bool IsDeferred
339         {
340             get { return HasSource; }
341         }
342
343         /// <summary>
344         /// Returns true if values have been either assigned or loaded.
345         /// </summary>
346         internal bool HasValues {
347             get { return this.source == null || this.HasAssignedValues || this.HasLoadedValues; }
348         }
349
350         /// <summary>
351         /// Returns true if the entity set has been modified in any way by the user or its items
352         /// have been loaded from the database.
353         /// </summary>
354         public bool HasLoadedOrAssignedValues {
355             get { return this.HasAssignedValues || this.HasLoadedValues; }
356         }
357
358         /// <summary>   
359         /// Returns true if the set has been modified in any way by the user.
360         /// </summary>
361         internal bool HasAssignedValues {
362             get { return this.isModified; }
363         }
364
365         /// <summary>
366         /// Returns true if the set has been loaded from the database.
367         /// </summary>
368         internal bool HasLoadedValues {
369             get { return this.isLoaded; }
370         }
371
372         /// <summary>
373         /// Returns true if the set has a deferred source query that hasn't been loaded yet.
374         /// </summary>
375         internal bool HasSource {
376             get { return this.source != null && !this.HasLoadedValues; }
377         }
378
379         /// <summary>
380         /// Returns true if the collection has been loaded.
381         /// </summary>
382         internal bool IsLoaded {
383             get {
384                 return this.isLoaded;
385             }
386         }
387
388         internal IEnumerable<TEntity> Source {
389             get { return this.source; }
390         }
391
392         public void Load() {
393             if (this.HasSource) {
394                 ItemList<TEntity> addedEntities = entities;
395                 entities = default(ItemList<TEntity>);
396                 foreach (TEntity e in source) entities.Add(e);
397                 foreach (TEntity e in addedEntities) entities.Include(e);
398                 foreach (TEntity e in removedEntities) entities.Remove(e);
399                 source = SourceState<TEntity>.Loaded;
400                 isLoaded = true;
401                 removedEntities = default(ItemList<TEntity>);
402             }
403         }
404
405         private void OnModified() {
406             isModified = true;
407         }
408
409         [SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Justification = "[....]: Naming the parameter entity makes it more discoverable because it is clear what type of data should be added to this collection.")]
410         public bool Remove(TEntity entity) {
411             if (entity == null || entity == onRemoveEntity) return false;
412             CheckModify();
413             int index = -1;
414             bool removed = false;
415             if (this.HasSource) {
416                 if (!removedEntities.Contains(entity)) {
417                     OnRemove(entity);
418                     // check in entities in case it has been pre-added
419                     index = entities.IndexOf(entity);
420                     if (index != -1) {
421                         entities.RemoveAt(index);
422                     }
423                     else {
424                         removedEntities.Add(entity);
425                     }
426                     removed = true;
427                 }
428             } else {
429                 index = entities.IndexOf(entity);
430                 if (index != -1) {
431                     OnRemove(entity);
432                     entities.RemoveAt(index);
433                     removed = true;
434                 }
435             }
436             if (removed) {
437                 OnModified();
438                 // If index == -1 here, that means that the entity was not in the list before Remove was called,
439                 // so we shouldn't fire the event since the list itself will not be changed, even though the Remove will still be tracked
440                 // on the removedEntities list in case a subsequent Load brings in this entity.
441                 if (index != -1) {                    
442                     OnListChanged(ListChangedType.ItemDeleted, index);
443                 }
444             }
445             return removed;
446         }
447
448         public void RemoveAt(int index) {
449             Load();
450             if (index < 0 || index >= Count) {
451                 throw Error.ArgumentOutOfRange("index");
452             }
453             CheckModify();
454             TEntity entity = entities[index];
455             OnRemove(entity);
456             entities.RemoveAt(index);
457             OnModified();
458             OnListChanged(ListChangedType.ItemDeleted, index);
459         }
460
461         public void SetSource(IEnumerable<TEntity> entitySource) {
462             if (this.HasAssignedValues || this.HasLoadedValues)
463                 throw Error.EntitySetAlreadyLoaded();
464             this.source = entitySource;
465         }
466
467         void CheckModify() {
468             if (onAddEntity != null || onRemoveEntity != null)
469                 throw Error.ModifyDuringAddOrRemove();
470             version++;
471         }
472
473         void OnAdd(TEntity entity) {
474             if (onAdd != null) {
475                 TEntity e = onAddEntity;
476                 onAddEntity = entity;
477                 try {
478                     onAdd(entity);
479                 } finally {
480                     onAddEntity = e;
481                 }
482             }
483         }
484
485         void OnRemove(TEntity entity) {
486             if (onRemove != null) {
487                 TEntity e = onRemoveEntity;
488                 onRemoveEntity = entity;
489                 try {
490                     onRemove(entity);
491                 } finally {
492                     onRemoveEntity = e;
493                 }
494             }
495         }
496
497         class Enumerable : IEnumerable<TEntity> {
498             EntitySet<TEntity> entitySet;
499             public Enumerable(EntitySet<TEntity> entitySet) {
500                 this.entitySet = entitySet;
501             }
502             IEnumerator IEnumerable.GetEnumerator() {
503                 return this.GetEnumerator();
504             }
505             public IEnumerator<TEntity> GetEnumerator() {
506                 return new Enumerator(this.entitySet);
507             }
508         }
509
510         class Enumerator : IEnumerator<TEntity> {
511             EntitySet<TEntity> entitySet;
512             TEntity[] items;
513             int index;
514             int endIndex;
515             int version;
516
517             public Enumerator(EntitySet<TEntity> entitySet) {
518                 this.entitySet = entitySet;
519                 this.items = entitySet.entities.Items;
520                 this.index = -1;
521                 this.endIndex = entitySet.entities.Count - 1;
522                 this.version = entitySet.version;
523             }
524
525             public void Dispose()
526             {
527                 // Technically, calling GC.SuppressFinalize is not required because the class does not
528                 // have a finalizer, but it does no harm, protects against the case where a finalizer is added
529                 // in the future, and prevents an FxCop warning.
530                 GC.SuppressFinalize(this);
531             }
532
533             public bool MoveNext() {
534                 if (version != entitySet.version)
535                     throw Error.EntitySetModifiedDuringEnumeration();
536                 if (index == endIndex) return false;
537                 index++;
538                 return true;
539             }
540
541             public TEntity Current {
542                 get { return items[index]; }
543             }
544
545             object IEnumerator.Current {
546                 get { return items[index]; }
547             }
548
549             void IEnumerator.Reset() {
550                 if (version != entitySet.version)
551                     throw Error.EntitySetModifiedDuringEnumeration();
552                 index = -1;
553             }
554         }
555
556         int IList.Add(object value) {
557             TEntity entity = value as TEntity;
558             if (entity == null || IndexOf(entity) >= 0) {
559                 throw Error.ArgumentOutOfRange("value");
560             }
561             CheckModify();
562             int i = entities.Count;
563             entities.Add(entity);
564             OnAdd(entity);
565             return i;
566         }
567
568         bool IList.Contains(object value) {
569             return Contains(value as TEntity);
570         }
571
572         int IList.IndexOf(object value) {
573             return IndexOf(value as TEntity);
574         }
575
576         void IList.Insert(int index, object value) {
577             TEntity entity = value as TEntity;
578             if (value == null)
579                 throw Error.ArgumentOutOfRange("value");
580             Insert(index, entity);
581         }
582
583         bool IList.IsFixedSize {
584             get { return false; }
585         }
586
587         bool IList.IsReadOnly {
588             get { return false; }
589         }
590
591         void IList.Remove(object value) {
592             Remove(value as TEntity);
593         }
594
595         object IList.this[int index] {
596             get {
597                 return this[index];
598             }
599             set {
600                 TEntity entity = value as TEntity;
601                 if (value == null) throw Error.ArgumentOutOfRange("value");
602                 this[index] = entity;
603             }
604         }
605
606         void ICollection.CopyTo(Array array, int index) {
607             Load();
608             if (entities.Count > 0) Array.Copy(entities.Items, 0, array, index, entities.Count);
609         }
610
611         bool ICollection.IsSynchronized {
612             get { return false; }
613         }
614
615         object ICollection.SyncRoot {
616             get { return this; }
617         }
618
619         bool ICollection<TEntity>.IsReadOnly {
620             get { return false; }
621         }
622
623         IEnumerator IEnumerable.GetEnumerator() {
624             return GetEnumerator();
625         }
626
627         void OnListChanged(ListChangedType type, int index) {
628             listChanged = true;
629             if (onListChanged != null) {
630                 onListChanged(this, new ListChangedEventArgs(type, index));
631             }
632         }
633
634         public event ListChangedEventHandler ListChanged {
635             add {
636                 onListChanged += value;
637             }
638             remove {
639                 onListChanged -= value;
640             }
641         }
642
643         bool IListSource.ContainsListCollection {
644             get { return true; }
645         }
646
647         private IBindingList cachedList = null;
648
649         IList IListSource.GetList() {
650             if (cachedList == null || listChanged) {
651                 cachedList = GetNewBindingList();
652                 listChanged = false;
653             }
654             return cachedList;
655         }
656
657         [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification="Method doesn't represent a property of the type.")]
658         public IBindingList GetNewBindingList() {
659             return new EntitySetBindingList<TEntity>(this.ToList(), this);
660         }
661     }
662
663     struct ItemList<T> where T : class {
664         T[] items;
665         int count;
666
667         public int Count {
668             get { return count; }
669         }
670
671         public T[] Items {
672             get { return items; }
673         }
674
675         public T this[int index] {
676             get { return items[index]; }
677             set { items[index] = value; }
678         }
679
680         public void Add(T item) {
681             if (items == null || items.Length == count) GrowItems();
682             items[count] = item;
683             count++;
684         }
685
686         public bool Contains(T item) {
687             return IndexOf(item) >= 0;
688         }
689
690         public Enumerator GetEnumerator() {
691             Enumerator e;
692             e.items = items;
693             e.index = -1;
694             e.endIndex = count - 1;
695             return e;
696         }
697
698         public bool Include(T item) {
699             if (LastIndexOf(item) >= 0) return false;
700             Add(item);
701             return true;
702         }
703
704         public int IndexOf(T item) {
705             for (int i = 0; i < count; i++) {
706                 if (items[i] == item) return i;
707             }
708             return -1;
709         }
710
711         public void Insert(int index, T item) {
712             if (items == null || items.Length == count) GrowItems();
713             if (index < count) Array.Copy(items, index, items, index + 1, count - index);
714             items[index] = item;
715             count++;
716         }
717
718         public int LastIndexOf(T item) {
719             int i = count;
720             while (i > 0) {
721                 --i;
722                 if (items[i] == item) return i;
723             }
724             return -1;
725         }
726
727         public bool Remove(T item) {
728             int i = IndexOf(item);
729             if (i < 0) return false;
730             RemoveAt(i);
731             return true;
732         }
733
734         public void RemoveAt(int index) {
735             count--;
736             if (index < count) Array.Copy(items, index + 1, items, index, count - index);
737             items[count] = default(T);
738         }
739
740         void GrowItems() {
741             Array.Resize(ref items, count == 0 ? 4 : count * 2);
742         }
743
744         public struct Enumerator {
745             internal T[] items;
746             internal int index;
747             internal int endIndex;
748
749             public bool MoveNext() {
750                 if (index == endIndex) return false;
751                 index++;
752                 return true;
753             }
754
755             public T Current {
756                 get { return items[index]; }
757             }
758         }
759     }
760
761     [SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces", Justification = "[....]: The name clearly describes function and the namespace is under a DLinq namespace which will make the distinction clear.")]
762     [DataContract]
763     [Serializable]
764     public sealed class Binary : IEquatable<Binary> {
765         [DataMember(Name="Bytes")]
766         byte[] bytes;
767         int? hashCode;
768
769         public Binary(byte[] value) {
770             if (value == null) {
771                 this.bytes = new byte[0];
772             }
773             else {
774                 this.bytes = new byte[value.Length];
775                 Array.Copy(value, this.bytes, value.Length);
776             }
777             this.ComputeHash();
778         }
779
780         public byte[] ToArray() {
781             byte[] copy = new byte[this.bytes.Length];
782             Array.Copy(this.bytes, copy, copy.Length);
783             return copy;
784         }
785
786         public int Length {
787             get { return this.bytes.Length; }
788         }
789
790         public static implicit operator Binary(byte[] value) {
791             return new Binary(value);
792         }
793
794         public bool Equals(Binary other) {
795             return this.EqualsTo(other);
796         }
797
798         public static bool operator ==(Binary binary1, Binary binary2) {
799             if ((object)binary1 == (object)binary2)
800                 return true;
801             if ((object)binary1 == null && (object)binary2 == null)
802                 return true;
803             if ((object)binary1 == null || (object)binary2 == null)
804                 return false;
805             return binary1.EqualsTo(binary2);
806         }
807
808         public static bool operator !=(Binary binary1, Binary binary2) {
809             if ((object)binary1 == (object)binary2)
810                 return false;
811             if ((object)binary1 == null && (object)binary2 == null)
812                 return false;
813             if ((object)binary1 == null || (object)binary2 == null)
814                 return true;
815             return !binary1.EqualsTo(binary2);
816         }
817
818         public override bool Equals(object obj) {
819             return this.EqualsTo(obj as Binary);
820         }
821
822         public override int GetHashCode() {
823             if (!hashCode.HasValue) {
824                 // hash code is not marked [DataMember], so when
825                 // using the DataContractSerializer, we'll need
826                 // to recompute the hash after deserialization.
827                 ComputeHash();
828             }
829             return this.hashCode.Value;
830         }
831
832         public override string ToString() {
833             StringBuilder sb = new StringBuilder();
834             sb.Append("\"");
835             sb.Append(System.Convert.ToBase64String(this.bytes, 0, this.bytes.Length));
836             sb.Append("\"");
837             return sb.ToString();
838         }
839
840         private bool EqualsTo(Binary binary) {
841             if ((object)this == (object)binary)
842                 return true;
843             if ((object)binary == null)
844                 return false;
845             if (this.bytes.Length != binary.bytes.Length)
846                 return false;
847             if (this.GetHashCode() != binary.GetHashCode())
848                 return false;
849             for (int i = 0, n = this.bytes.Length; i < n; i++) {
850                 if (this.bytes[i] != binary.bytes[i])
851                     return false;
852             }
853             return true;
854         }
855
856         /// <summary>
857         /// Simple hash using pseudo-random coefficients for each byte in 
858         /// the array to achieve order dependency.
859         /// </summary>
860         private void ComputeHash() {
861             int s = 314, t = 159;
862             hashCode = 0;
863             for (int i = 0; i < bytes.Length; i++) {
864                 hashCode = hashCode * s + bytes[i];
865                 s = s * t;
866             }
867         }
868     }
869 }