@@ -46,7 +46,7 @@ public static partial class AutoMapper
4646 /// <summary>
4747 /// Contains the methods and members responsible for this libraries internal concerns.
4848 /// </summary>
49- public static class InternalHelpers
49+ internal static class InternalHelpers
5050 {
5151 /// <summary>
5252 /// Gets the identifiers for the given type. Returns NULL if not found.
@@ -298,7 +298,7 @@ private static object ConvertValuesTypeToMembersType(object value, string member
298298 /// <param name="member">FieldInfo or PropertyInfo object</param>
299299 /// <param name="obj">Object to get the value from</param>
300300 /// <returns>Value of the member</returns>
301- public static object GetMemberValue ( object member , object obj )
301+ private static object GetMemberValue ( object member , object obj )
302302 {
303303 object value = null ;
304304
@@ -327,22 +327,43 @@ public static object GetMemberValue(object member, object obj)
327327 /// </summary>
328328 /// <param name="type">Type of instance to get</param>
329329 /// <param name="properties">List of properties and values</param>
330- /// <param name="parentHash">Hash from parent object </param>
330+ /// <param name="parentInstance">Parent instance. Can be NULL if this is the root instance. </param>
331331 /// <returns>
332332 /// Tuple of bool, object, int where bool represents whether this is a newly created instance,
333333 /// object being an instance of the requested type and int being the instance's identifier hash.
334334 /// </returns>
335- public static Tuple < bool , object , int > GetInstance ( Type type , IDictionary < string , object > properties , int parentHash )
335+ internal static Tuple < bool , object , Tuple < int , int , object > > GetInstance ( Type type , IDictionary < string , object > properties , object parentInstance = null )
336336 {
337+ var key = GetCacheKey ( type , properties , parentInstance ) ;
338+
337339 var instanceCache = Cache . GetInstanceCache ( ) ;
338340
339- var identifiers = GetIdentifiers ( type ) ;
341+ object instance ;
342+
343+ var isNewlyCreatedInstance = ! instanceCache . TryGetValue ( key , out instance ) ;
344+
345+ if ( isNewlyCreatedInstance )
346+ {
347+ instance = CreateInstance ( type ) ;
348+ instanceCache [ key ] = instance ;
349+ }
350+
351+ return Tuple . Create ( isNewlyCreatedInstance , instance , key ) ;
352+ }
340353
341- object instance = null ;
354+ private static Tuple < int , int , object > GetCacheKey ( Type type , IDictionary < string , object > properties , object parentInstance )
355+ {
356+ var identifierHash = GetIdentifierHash ( type , properties ) ;
342357
343- bool isNewlyCreatedInstance = false ;
358+ var key = Tuple . Create ( identifierHash , type . GetHashCode ( ) , parentInstance ) ;
359+ return key ;
360+ }
344361
345- int identifierHash = 0 ;
362+ private static int GetIdentifierHash ( Type type , IDictionary < string , object > properties )
363+ {
364+ var identifiers = GetIdentifiers ( type ) ;
365+
366+ var identifierHash = 0 ;
346367
347368 if ( identifiers != null )
348369 {
@@ -352,40 +373,23 @@ public static Tuple<bool, object, int> GetInstance(Type type, IDictionary<string
352373 {
353374 var identifierValue = properties [ identifier ] ;
354375 if ( identifierValue != null )
355- identifierHash += identifierValue . GetHashCode ( ) + type . GetHashCode ( ) + parentHash ;
356- }
357- }
358-
359- if ( identifierHash != 0 )
360- {
361- if ( instanceCache . ContainsKey ( identifierHash ) )
362- {
363- instance = instanceCache [ identifierHash ] ;
364- }
365- else
366- {
367- instance = CreateInstance ( type ) ;
368-
369- instanceCache . Add ( identifierHash , instance ) ;
370-
371- isNewlyCreatedInstance = true ;
376+ {
377+ // Unchecked to avoid arithmetic overflow
378+ unchecked
379+ {
380+ // Include identifier hashcode to avoid collisions between e.g. multiple int IDs
381+ identifierHash += identifierValue . GetHashCode ( ) + identifier . GetHashCode ( ) ;
382+ }
383+ }
372384 }
373385 }
374386 }
375-
376- // An identifier hash with a value of zero means the type does not have any identifiers.
377- // To make this instance unique generate a unique hash for it.
378- if ( identifierHash == 0 && identifiers != null ) identifierHash = type . GetHashCode ( ) + parentHash ;
379-
380- if ( instance == null )
387+ else
381388 {
382- instance = CreateInstance ( type ) ;
389+ // If the type has no identifiers we must generate a unique hash for it.
383390 identifierHash = Guid . NewGuid ( ) . GetHashCode ( ) ;
384-
385- isNewlyCreatedInstance = true ;
386391 }
387-
388- return new Tuple < bool , object , int > ( isNewlyCreatedInstance , instance , identifierHash ) ;
392+ return identifierHash ;
389393 }
390394
391395 /// <summary>
@@ -399,7 +403,7 @@ public static Tuple<bool, object, int> GetInstance(Type type, IDictionary<string
399403 /// <param name="instance">Instance to populate</param>
400404 /// <param name="parentInstance">Optional parent instance of the instance being populated</param>
401405 /// <returns>Populated instance</returns>
402- public static object Map ( IDictionary < string , object > dictionary , object instance , object parentInstance = null )
406+ internal static object Map ( IDictionary < string , object > dictionary , object instance , object parentInstance = null )
403407 {
404408 if ( instance . GetType ( ) . IsPrimitive || instance is string )
405409 {
@@ -471,7 +475,7 @@ public static object Map(IDictionary<string, object> dictionary, object instance
471475 }
472476 else
473477 {
474- var result = GetInstance ( memberType , newDictionary , parentInstance == null ? 0 : parentInstance . GetHashCode ( ) ) ;
478+ var result = GetInstance ( memberType , newDictionary , parentInstance ) ;
475479 nestedInstance = result . Item2 ;
476480 }
477481 }
@@ -509,7 +513,7 @@ public static object Map(IDictionary<string, object> dictionary, object instance
509513 /// <param name="instance">Instance to populate</param>
510514 /// <param name="parentInstance">Optional parent instance of the instance being populated</param>
511515 /// <returns>Populated instance</returns>
512- public static object MapCollection ( Type type , IDictionary < string , object > dictionary , object instance , object parentInstance = null )
516+ internal static object MapCollection ( Type type , IDictionary < string , object > dictionary , object instance , object parentInstance = null )
513517 {
514518 Type baseListType = typeof ( List < > ) ;
515519
@@ -526,7 +530,7 @@ public static object MapCollection(Type type, IDictionary<string, object> dictio
526530 return instance ;
527531 }
528532
529- var getInstanceResult = GetInstance ( type , dictionary , parentInstance == null ? 0 : parentInstance . GetHashCode ( ) ) ;
533+ var getInstanceResult = GetInstance ( type , dictionary , parentInstance ) ;
530534
531535 // Is this a newly created instance? If false, then this item was retrieved from the instance cache.
532536 bool isNewlyCreatedInstance = getInstanceResult . Item1 ;
@@ -582,7 +586,7 @@ public static object MapCollection(Type type, IDictionary<string, object> dictio
582586 /// Provides a means of getting/storing data in the host application's
583587 /// appropriate context.
584588 /// </summary>
585- public interface IContextStorage
589+ internal interface IContextStorage
586590 {
587591 /// <summary>
588592 /// Get a stored item.
@@ -683,7 +687,7 @@ public void Remove(string key)
683687 /// For ASP.NET applications, it will store in the data in the current HTTPContext.
684688 /// For all other applications, it will store the data in the logical call context.
685689 /// </remarks>
686- public static class ContextStorage
690+ internal static class ContextStorage
687691 {
688692 /// <summary>
689693 /// Provides a means of getting/storing data in the host application's
@@ -730,7 +734,7 @@ public static void Remove(string key)
730734 /// <summary>
731735 /// Contains the methods and members responsible for this libraries reflection concerns.
732736 /// </summary>
733- public static class ReflectionHelper
737+ private static class ReflectionHelper
734738 {
735739 /// <summary>
736740 /// Provides access to System.Web.HttpContext.Current.Items via reflection.
0 commit comments