@@ -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 ;
340342
341- object instance = null ;
343+ var isNewlyCreatedInstance = ! instanceCache . TryGetValue ( key , out instance ) ;
344+
345+ if ( isNewlyCreatedInstance )
346+ {
347+ instance = CreateInstance ( type ) ;
348+ instanceCache [ key ] = instance ;
349+ }
342350
343- bool isNewlyCreatedInstance = false ;
351+ return Tuple . Create ( isNewlyCreatedInstance , instance , key ) ;
352+ }
353+
354+ private static Tuple < int , int , object > GetCacheKey ( Type type , IDictionary < string , object > properties , object parentInstance )
355+ {
356+ var identifierHash = GetIdentifierHash ( type , properties ) ;
357+
358+ var key = Tuple . Create ( identifierHash , type . GetHashCode ( ) , parentInstance ) ;
359+ return key ;
360+ }
361+
362+ private static int GetIdentifierHash ( Type type , IDictionary < string , object > properties )
363+ {
364+ var identifiers = GetIdentifiers ( type ) ;
344365
345- int identifierHash = 0 ;
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 )
360376 {
361- if ( instanceCache . ContainsKey ( identifierHash ) )
377+ // Unchecked to avoid arithmetic overflow
378+ unchecked
362379 {
363- instance = instanceCache [ identifierHash ] ;
380+ // Include identifier hashcode to avoid collisions between e.g. multiple int IDs
381+ identifierHash += identifierValue . GetHashCode ( ) + identifier . GetHashCode ( ) ;
382+ }
364383 }
365- else
366- {
367- instance = CreateInstance ( type ) ;
368-
369- instanceCache . Add ( identifierHash , instance ) ;
370-
371- isNewlyCreatedInstance = true ;
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 {
@@ -508,7 +512,7 @@ public static object Map(IDictionary<string, object> dictionary, object instance
508512 /// <param name="instance">Instance to populate</param>
509513 /// <param name="parentInstance">Optional parent instance of the instance being populated</param>
510514 /// <returns>Populated instance</returns>
511- public static object MapCollection ( Type type , IDictionary < string , object > dictionary , object instance , object parentInstance = null )
515+ internal static object MapCollection ( Type type , IDictionary < string , object > dictionary , object instance , object parentInstance = null )
512516 {
513517 Type baseListType = typeof ( List < > ) ;
514518
@@ -525,7 +529,7 @@ public static object MapCollection(Type type, IDictionary<string, object> dictio
525529 return instance ;
526530 }
527531
528- var getInstanceResult = GetInstance ( type , dictionary , parentInstance == null ? 0 : parentInstance . GetHashCode ( ) ) ;
532+ var getInstanceResult = GetInstance ( type , dictionary , parentInstance ) ;
529533
530534 // Is this a newly created instance? If false, then this item was retrieved from the instance cache.
531535 bool isNewlyCreatedInstance = getInstanceResult . Item1 ;
@@ -581,7 +585,7 @@ public static object MapCollection(Type type, IDictionary<string, object> dictio
581585 /// Provides a means of getting/storing data in the host application's
582586 /// appropriate context.
583587 /// </summary>
584- public interface IContextStorage
588+ internal interface IContextStorage
585589 {
586590 /// <summary>
587591 /// Get a stored item.
@@ -682,7 +686,7 @@ public void Remove(string key)
682686 /// For ASP.NET applications, it will store in the data in the current HTTPContext.
683687 /// For all other applications, it will store the data in the logical call context.
684688 /// </remarks>
685- public static class ContextStorage
689+ internal static class ContextStorage
686690 {
687691 /// <summary>
688692 /// Provides a means of getting/storing data in the host application's
@@ -729,7 +733,7 @@ public static void Remove(string key)
729733 /// <summary>
730734 /// Contains the methods and members responsible for this libraries reflection concerns.
731735 /// </summary>
732- public static class ReflectionHelper
736+ private static class ReflectionHelper
733737 {
734738 /// <summary>
735739 /// Provides access to System.Web.HttpContext.Current.Items via reflection.
0 commit comments