3 Copyright (c) 2003-2004 Niels Kokholm <kokholm@itu.dk> and Peter Sestoft <sestoft@dina.kvl.dk>
\r
4 Permission is hereby granted, free of charge, to any person obtaining a copy
\r
5 of this software and associated documentation files (the "Software"), to deal
\r
6 in the Software without restriction, including without limitation the rights
\r
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
\r
8 copies of the Software, and to permit persons to whom the Software is
\r
9 furnished to do so, subject to the following conditions:
\r
11 The above copyright notice and this permission notice shall be included in
\r
12 all copies or substantial portions of the Software.
\r
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
\r
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
\r
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
\r
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
\r
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
\r
25 using System.Reflection;
\r
26 using System.Reflection.Emit;
\r
27 using System.Diagnostics;
\r
28 namespace C5.ComparerBuilder
\r
31 /// A default item comparer for an item type that is either generic (IComparable<T>)
\r
32 /// or ordinarily (System.IComparable) comparable.
\r
34 public class FromComparable<T>
\r
36 static Type naturalComparerO = typeof(NaturalComparerO<>);
\r
38 static Type naturalComparer = typeof(NaturalComparer<>);
\r
42 /// Create a default comparer
\r
43 /// <exception cref="ArgumentException"/> if T is not comparable.
\r
45 /// <returns>The comparer</returns>
\r
47 public static IComparer<T> Examine()
\r
51 if (t.Equals(typeof(int)))
\r
52 return (IComparer<T>)(new IC());
\r
54 if (typeof(IComparable<T>).IsAssignableFrom(t))
\r
56 Type c = naturalComparer.BindGenericParameters(new Type[] { t });
\r
58 return (IComparer<T>)(c.GetConstructor(System.Type.EmptyTypes).Invoke(null));
\r
61 if (t.GetInterface("System.IComparable") != null)
\r
63 Type c = naturalComparerO.BindGenericParameters(new Type[] { t });
\r
65 return (IComparer<T>)(c.GetConstructor(System.Type.EmptyTypes).Invoke(null));
\r
68 throw new ArgumentException(String.Format("Cannot make IComparer<{0}>", t));
\r
72 namespace C5.HasherBuilder
\r
75 /// Prototype for an sequenced hasher for IIndexed[W]
\r
76 /// This will use the IIndexed[W] specific operations
\r
78 public class SequencedHasher<S, W> : IHasher<S>
\r
79 where S : ISequenced<W>
\r
82 /// Get the hash code with respect to this sequenced hasher
\r
84 /// <param name="item">The item</param>
\r
85 /// <returns>The hash code</returns>
\r
87 public int GetHashCode(S item) { return item.GetHashCode(); }
\r
91 /// Check if two items are equal with respect to this sequenced hasher
\r
93 /// <param name="i1">first item</param>
\r
94 /// <param name="i2">second item</param>
\r
95 /// <returns>True if equal</returns>
\r
97 public bool Equals(S i1, S i2) { return i1 == null ? i2 == null : i1.Equals(i2); }
\r
103 /// Prototype for an unsequenced hasher for ICollection[W]
\r
104 /// This will use the ICollection[W] specific operations
\r
106 public class UnsequencedHasher<S, W> : IHasher<S>
\r
107 where S : ICollection<W>
\r
110 /// Get the hash code with respect to this unsequenced hasher
\r
112 /// <param name="item">The item</param>
\r
113 /// <returns>The hash code</returns>
\r
115 public int GetHashCode(S item) { return item.GetHashCode(); }
\r
119 /// Check if two items are equal with respect to this unsequenced hasher
\r
121 /// <param name="i1">first item</param>
\r
122 /// <param name="i2">second item</param>
\r
123 /// <returns>True if equal</returns>
\r
125 public bool Equals(S i1, S i2) { return i1 == null ? i2 == null : i1.Equals(i2); }
\r
131 /// Create a hasher for T that is DefaultValueTypeHasher[T]
\r
132 /// or DefaultReferenceTypeHasher[T] unless T has been
\r
133 /// instatiated to a type of the exact form IIndexed[W] or ICollection[W]
\r
134 /// in which case Examine will return Sequenced- repectively UnsequencedHasher.
\r
136 public class ByPrototype<T>
\r
138 static Type isequenced = typeof(ISequenced<>);
\r
140 static Type ieditable = typeof(ICollection<>);
\r
142 static Type orderedhasher = typeof(HasherBuilder.SequencedHasher<,>);
\r
144 static Type unorderedhasher = typeof(HasherBuilder.UnsequencedHasher<,>);
\r
146 static Type isequenced = Type.GetType("C5.ISequenced");
\r
148 static Type ieditable = Type.GetType("C5.ICollection");
\r
150 static Type orderedhasher = Type.GetType("C5.HasherBuilder.SequencedHasher");
\r
152 static Type unorderedhasher = Type.GetType("C5.HasherBuilder.UnsequencedHasher");
\r
156 /// See class description
\r
158 /// <returns>The hasher</returns>
\r
160 public static IHasher<T> Examine()
\r
162 Type t = typeof(T);
\r
164 if (!t.HasGenericArguments)
\r
166 if (t.Equals(typeof(int)))
\r
167 return (IHasher<T>)(new IntHasher());
\r
168 else if (t.IsValueType)
\r
169 return new DefaultValueTypeHasher<T>();
\r
171 return new DefaultReferenceTypeHasher<T>();
\r
174 Type s = t.GetGenericTypeDefinition();
\r
175 Type[] v = t.GetGenericArguments();
\r
178 if (s.Equals(isequenced))
\r
180 else if (s.Equals(ieditable))
\r
181 b = unorderedhasher;
\r
182 else if (t.IsValueType)
\r
183 return new DefaultValueTypeHasher<T>();
\r
185 return new DefaultReferenceTypeHasher<T>();
\r
187 Type c = b.BindGenericParameters(new Type[] { t, v[0] });
\r
189 return (IHasher<T>)(c.GetConstructor(System.Type.EmptyTypes).Invoke(null));
\r
197 /// IHasher factory class: examines at instatiation time if T is an
\r
198 /// interface implementing "int GetHashCode()" and "bool Equals(T)".
\r
199 /// If those are not present, MakeHasher will return a default hasher,
\r
200 /// else this class will implement Ihasher[T] via Invoke() on the
\r
201 /// reflected method infos.
\r
203 public class ByInvoke<T> : IHasher<T>
\r
205 internal static readonly System.Reflection.MethodInfo hinfo, einfo;
\r
210 Type t = typeof(T);
\r
212 if (!t.IsInterface) return;
\r
214 BindingFlags f = BindingFlags.Public | BindingFlags.Instance;
\r
216 hinfo = t.GetMethod("GetHashCode", f, null, new Type[0], null);
\r
217 einfo = t.GetMethod("Equals", f, null, new Type[1] { t }, null);
\r
221 private ByInvoke() { }
\r
226 /// <returns></returns>
\r
227 public static IHasher<T> MakeHasher()
\r
229 if (hinfo != null && einfo != null)
\r
230 return new ByInvoke<T>();
\r
232 return new DefaultReferenceTypeHasher<T>();
\r
238 /// <param name="item"></param>
\r
239 /// <returns></returns>
\r
240 public int GetHashCode(T item)
\r
242 return (int)(hinfo.Invoke(item, null));
\r
248 /// <param name="i1"></param>
\r
249 /// <param name="i2"></param>
\r
250 /// <returns></returns>
\r
251 public bool Equals(T i1, T i2)
\r
253 return (bool)(einfo.Invoke(i1, new object[1] { i2 }));
\r
260 /// Like ByInvoke, but tries to build a hasher by RTCG to
\r
261 /// avoid the Invoke() overhead. Does not work as intended
\r
262 /// because of a Whidbey RTCG bug.
\r
264 public class ByRTCG
\r
266 private static ModuleBuilder moduleBuilder;
\r
268 private static AssemblyBuilder assemblyBuilder;
\r
270 private static int uid = 0;
\r
276 /// <param name="hinfo"></param>
\r
277 /// <param name="einfo"></param>
\r
278 /// <returns></returns>
\r
279 public static /*ObjectHasher */ IHasher<T> CreateHasher<T>(MethodInfo hinfo, MethodInfo einfo)
\r
281 if (moduleBuilder == null)
\r
283 string assmname = "LeFake";
\r
284 string filename = assmname + ".dll";
\r
285 AssemblyName assemblyName = new AssemblyName("LeFake");
\r
286 AppDomain appdomain = AppDomain.CurrentDomain;
\r
287 AssemblyBuilderAccess acc = AssemblyBuilderAccess.RunAndSave;
\r
289 assemblyBuilder = appdomain.DefineDynamicAssembly(assemblyName, acc);
\r
290 moduleBuilder = assemblyBuilder.DefineDynamicModule(assmname, filename);
\r
293 Type t = typeof(/*object*/ T);
\r
294 Type o_t = typeof(object);
\r
295 Type h_t = typeof(/*ObjectHasher*/ IHasher<T>);
\r
296 Type i_t = typeof(int);
\r
297 //TODO: protect uid for thread safety!
\r
298 string name = "C5.Dynamic.Hasher_" + uid++;
\r
299 TypeAttributes tatt = TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed;
\r
300 TypeBuilder tb = moduleBuilder.DefineType(name, tatt, o_t, new Type[1] { h_t });
\r
301 MethodAttributes matt = MethodAttributes.Public | MethodAttributes.Virtual;
\r
302 MethodBuilder mb = tb.DefineMethod("GetHashCode", matt, i_t, new Type[1] { t });
\r
303 ILGenerator ilg = mb.GetILGenerator();
\r
305 ilg.Emit(OpCodes.Ldarg_1);
\r
306 ilg.Emit(OpCodes.Callvirt, hinfo);
\r
307 ilg.Emit(OpCodes.Ret);
\r
308 mb = tb.DefineMethod("Equals", matt, typeof(bool), new Type[2] { t, t });
\r
309 ilg = mb.GetILGenerator();
\r
310 ilg.Emit(OpCodes.Ldarg_1);
\r
311 ilg.Emit(OpCodes.Ldarg_2);
\r
312 ilg.Emit(OpCodes.Callvirt, einfo);
\r
313 ilg.Emit(OpCodes.Ret);
\r
315 Type hasher_t = tb.CreateType();
\r
316 object hasher = hasher_t.GetConstructor(new Type[0]).Invoke(null);
\r
318 return (IHasher<T>)hasher;
\r
324 /// <typeparam name="T"></typeparam>
\r
325 /// <returns></returns>
\r
326 public static IHasher<T> build<T>()
\r
328 MethodInfo hinfo = ByInvoke<T>.hinfo, einfo = ByInvoke<T>.einfo;
\r
330 if (hinfo != null && einfo != null)
\r
331 return CreateHasher<T>(hinfo, einfo);
\r
333 return ByPrototype<T>.Examine();
\r
341 assemblyBuilder.Save("LeFake.dll");
\r