10
10
using System . Diagnostics . Contracts ;
11
11
//using System.Globalization;
12
12
using System . Runtime . CompilerServices ;
13
+ using System . Runtime . Serialization ;
13
14
14
15
namespace System . Collections . Generic
15
16
{
@@ -61,6 +62,36 @@ private static Comparer<T> CreateComparer()
61
62
}
62
63
}
63
64
}
65
+ else if ( t . IsEnum )
66
+ {
67
+ // Explicitly call Enum.GetUnderlyingType here. Although GetTypeCode
68
+ // ends up doing this anyway, we end up avoiding an unnecessary P/Invoke
69
+ // and virtual method call.
70
+ TypeCode underlyingTypeCode = Type . GetTypeCode ( Enum . GetUnderlyingType ( t ) ) ;
71
+
72
+ // Depending on the enum type, we need to special case the comparers so that we avoid boxing
73
+ // Specialize differently for signed/unsigned types so we avoid problems with large numbers
74
+ switch ( underlyingTypeCode )
75
+ {
76
+ case TypeCode . SByte :
77
+ case TypeCode . Int16 :
78
+ case TypeCode . Int32 :
79
+ result = RuntimeTypeHandle . CreateInstanceForAnotherGenericParameter ( ( RuntimeType ) typeof ( Int32EnumComparer < int > ) , t ) ;
80
+ break ;
81
+ case TypeCode . Byte :
82
+ case TypeCode . UInt16 :
83
+ case TypeCode . UInt32 :
84
+ result = RuntimeTypeHandle . CreateInstanceForAnotherGenericParameter ( ( RuntimeType ) typeof ( UInt32EnumComparer < uint > ) , t ) ;
85
+ break ;
86
+ // 64-bit enums: use UnsafeEnumCastLong
87
+ case TypeCode . Int64 :
88
+ result = RuntimeTypeHandle . CreateInstanceForAnotherGenericParameter ( ( RuntimeType ) typeof ( Int64EnumComparer < long > ) , t ) ;
89
+ break ;
90
+ case TypeCode . UInt64 :
91
+ result = RuntimeTypeHandle . CreateInstanceForAnotherGenericParameter ( ( RuntimeType ) typeof ( UInt64EnumComparer < ulong > ) , t ) ;
92
+ break ;
93
+ }
94
+ }
64
95
65
96
return result != null ?
66
97
( Comparer < T > ) result :
@@ -77,9 +108,15 @@ int IComparer.Compare(object x, object y) {
77
108
return 0 ;
78
109
}
79
110
}
80
-
111
+
112
+ // Note: although there is a lot of shared code in the following
113
+ // comparers, we do not incorporate it into a base class for perf
114
+ // reasons. Adding another base class (even one with no fields)
115
+ // means another generic instantiation, which can be costly esp.
116
+ // for value types.
117
+
81
118
[ Serializable ]
82
- internal class GenericComparer < T > : Comparer < T > where T : IComparable < T >
119
+ internal sealed class GenericComparer < T > : Comparer < T > where T : IComparable < T >
83
120
{
84
121
public override int Compare ( T x , T y ) {
85
122
if ( x != null ) {
@@ -91,18 +128,15 @@ public override int Compare(T x, T y) {
91
128
}
92
129
93
130
// Equals method for the comparer itself.
94
- public override bool Equals ( Object obj ) {
95
- GenericComparer < T > comparer = obj as GenericComparer < T > ;
96
- return comparer != null ;
97
- }
131
+ public override bool Equals ( Object obj ) =>
132
+ obj != null && GetType ( ) == obj . GetType ( ) ;
98
133
99
- public override int GetHashCode ( ) {
100
- return this . GetType ( ) . Name . GetHashCode ( ) ;
101
- }
134
+ public override int GetHashCode ( ) =>
135
+ GetType ( ) . GetHashCode ( ) ;
102
136
}
103
137
104
138
[ Serializable ]
105
- internal class NullableComparer < T > : Comparer < Nullable < T > > where T : struct , IComparable < T >
139
+ internal sealed class NullableComparer < T > : Comparer < T ? > where T : struct , IComparable < T >
106
140
{
107
141
public override int Compare ( Nullable < T > x , Nullable < T > y ) {
108
142
if ( x . HasValue ) {
@@ -114,37 +148,30 @@ public override int Compare(Nullable<T> x, Nullable<T> y) {
114
148
}
115
149
116
150
// Equals method for the comparer itself.
117
- public override bool Equals ( Object obj ) {
118
- NullableComparer < T > comparer = obj as NullableComparer < T > ;
119
- return comparer != null ;
120
- }
121
-
151
+ public override bool Equals ( Object obj ) =>
152
+ obj != null && GetType ( ) == obj . GetType ( ) ;
122
153
123
- public override int GetHashCode ( ) {
124
- return this . GetType ( ) . Name . GetHashCode ( ) ;
125
- }
154
+ public override int GetHashCode ( ) =>
155
+ GetType ( ) . GetHashCode ( ) ;
126
156
}
127
157
128
158
[ Serializable ]
129
- internal class ObjectComparer < T > : Comparer < T >
159
+ internal sealed class ObjectComparer < T > : Comparer < T >
130
160
{
131
161
public override int Compare ( T x , T y ) {
132
162
return System . Collections . Comparer . Default . Compare ( x , y ) ;
133
163
}
134
164
135
165
// Equals method for the comparer itself.
136
- public override bool Equals ( Object obj ) {
137
- ObjectComparer < T > comparer = obj as ObjectComparer < T > ;
138
- return comparer != null ;
139
- }
166
+ public override bool Equals ( Object obj ) =>
167
+ obj != null && GetType ( ) == obj . GetType ( ) ;
140
168
141
- public override int GetHashCode ( ) {
142
- return this . GetType ( ) . Name . GetHashCode ( ) ;
143
- }
169
+ public override int GetHashCode ( ) =>
170
+ GetType ( ) . GetHashCode ( ) ;
144
171
}
145
172
146
173
[ Serializable ]
147
- internal class ComparisonComparer < T > : Comparer < T >
174
+ internal sealed class ComparisonComparer < T > : Comparer < T >
148
175
{
149
176
private readonly Comparison < T > _comparison ;
150
177
@@ -156,4 +183,138 @@ public override int Compare(T x, T y) {
156
183
return _comparison ( x , y ) ;
157
184
}
158
185
}
186
+
187
+ // Enum comparers (specialized to avoid boxing)
188
+ // NOTE: Each of these needs to implement ISerializable
189
+ // and have a SerializationInfo/StreamingContext ctor,
190
+ // since we want to serialize as ObjectComparer for
191
+ // back-compat reasons (see below).
192
+
193
+ [ Serializable ]
194
+ internal sealed class Int32EnumComparer < T > : Comparer < T > , ISerializable where T : struct
195
+ {
196
+ public Int32EnumComparer ( )
197
+ {
198
+ Contract . Assert ( typeof ( T ) . IsEnum , "This type is only intended to be used to compare enums!" ) ;
199
+ }
200
+
201
+ // Used by the serialization engine.
202
+ private Int32EnumComparer ( SerializationInfo info , StreamingContext context ) { }
203
+
204
+ public override int Compare ( T x , T y )
205
+ {
206
+ int ix = JitHelpers . UnsafeEnumCast ( x ) ;
207
+ int iy = JitHelpers . UnsafeEnumCast ( y ) ;
208
+ return ix . CompareTo ( iy ) ;
209
+ }
210
+
211
+ // Equals method for the comparer itself.
212
+ public override bool Equals ( Object obj ) =>
213
+ obj != null && GetType ( ) == obj . GetType ( ) ;
214
+
215
+ public override int GetHashCode ( ) =>
216
+ GetType ( ) . GetHashCode ( ) ;
217
+
218
+ public void GetObjectData ( SerializationInfo info , StreamingContext context )
219
+ {
220
+ // Previously Comparer<T> was not specialized for enums,
221
+ // and instead fell back to ObjectComparer which uses boxing.
222
+ // Set the type as ObjectComparer here so code that serializes
223
+ // Comparer for enums will not break.
224
+ info . SetType ( typeof ( ObjectComparer < T > ) ) ;
225
+ }
226
+ }
227
+
228
+ [ Serializable ]
229
+ internal sealed class UInt32EnumComparer < T > : Comparer < T > , ISerializable where T : struct
230
+ {
231
+ public UInt32EnumComparer ( )
232
+ {
233
+ Contract . Assert ( typeof ( T ) . IsEnum , "This type is only intended to be used to compare enums!" ) ;
234
+ }
235
+
236
+ // Used by the serialization engine.
237
+ private UInt32EnumComparer ( SerializationInfo info , StreamingContext context ) { }
238
+
239
+ public override int Compare ( T x , T y )
240
+ {
241
+ uint ix = ( uint ) JitHelpers . UnsafeEnumCast ( x ) ;
242
+ uint iy = ( uint ) JitHelpers . UnsafeEnumCast ( y ) ;
243
+ return ix . CompareTo ( iy ) ;
244
+ }
245
+
246
+ // Equals method for the comparer itself.
247
+ public override bool Equals ( Object obj ) =>
248
+ obj != null && GetType ( ) == obj . GetType ( ) ;
249
+
250
+ public override int GetHashCode ( ) =>
251
+ GetType ( ) . GetHashCode ( ) ;
252
+
253
+ public void GetObjectData ( SerializationInfo info , StreamingContext context )
254
+ {
255
+ info . SetType ( typeof ( ObjectComparer < T > ) ) ;
256
+ }
257
+ }
258
+
259
+ [ Serializable ]
260
+ internal sealed class Int64EnumComparer < T > : Comparer < T > , ISerializable where T : struct
261
+ {
262
+ public Int64EnumComparer ( )
263
+ {
264
+ Contract . Assert ( typeof ( T ) . IsEnum , "This type is only intended to be used to compare enums!" ) ;
265
+ }
266
+
267
+ // Used by the serialization engine.
268
+ private Int64EnumComparer ( SerializationInfo info , StreamingContext context ) { }
269
+
270
+ public override int Compare ( T x , T y )
271
+ {
272
+ long lx = JitHelpers . UnsafeEnumCastLong ( x ) ;
273
+ long ly = JitHelpers . UnsafeEnumCastLong ( y ) ;
274
+ return lx . CompareTo ( ly ) ;
275
+ }
276
+
277
+ // Equals method for the comparer itself.
278
+ public override bool Equals ( Object obj ) =>
279
+ obj != null && GetType ( ) == obj . GetType ( ) ;
280
+
281
+ public override int GetHashCode ( ) =>
282
+ GetType ( ) . GetHashCode ( ) ;
283
+
284
+ public void GetObjectData ( SerializationInfo info , StreamingContext context )
285
+ {
286
+ info . SetType ( typeof ( ObjectComparer < T > ) ) ;
287
+ }
288
+ }
289
+
290
+ [ Serializable ]
291
+ internal sealed class UInt64EnumComparer < T > : Comparer < T > , ISerializable where T : struct
292
+ {
293
+ public UInt64EnumComparer ( )
294
+ {
295
+ Contract . Assert ( typeof ( T ) . IsEnum , "This type is only intended to be used to compare enums!" ) ;
296
+ }
297
+
298
+ // Used by the serialization engine.
299
+ private UInt64EnumComparer ( SerializationInfo info , StreamingContext context ) { }
300
+
301
+ public override int Compare ( T x , T y )
302
+ {
303
+ ulong lx = ( ulong ) JitHelpers . UnsafeEnumCastLong ( x ) ;
304
+ ulong ly = ( ulong ) JitHelpers . UnsafeEnumCastLong ( y ) ;
305
+ return lx . CompareTo ( ly ) ;
306
+ }
307
+
308
+ // Equals method for the comparer itself.
309
+ public override bool Equals ( Object obj ) =>
310
+ obj != null && GetType ( ) == obj . GetType ( ) ;
311
+
312
+ public override int GetHashCode ( ) =>
313
+ GetType ( ) . GetHashCode ( ) ;
314
+
315
+ public void GetObjectData ( SerializationInfo info , StreamingContext context )
316
+ {
317
+ info . SetType ( typeof ( ObjectComparer < T > ) ) ;
318
+ }
319
+ }
159
320
}
0 commit comments