1717 /// </summary>
1818 public static class Reflection
1919 {
20+ private const bool ShouldInheritAttributes = false ;
21+
2022 private static readonly ConcurrentDictionary < Type , ConstructorInfo > TypesWithOneConstructorCache = new ConcurrentDictionary < Type , ConstructorInfo > ( ) ;
2123 private static readonly ConcurrentDictionary < Type , object > TypeAttributesCache = new ConcurrentDictionary < Type , object > ( ) ;
22- private static readonly ConcurrentDictionary < MethodInfo , IEnumerable < object > > MethodAttributesCache = new ConcurrentDictionary < MethodInfo , IEnumerable < object > > ( ) ;
24+ private static readonly ConcurrentDictionary < Type , object > MethodAttributesCache = new ConcurrentDictionary < Type , object > ( ) ;
2325 private static readonly ConcurrentDictionary < Type , string > FriendlyTypeNames = new ConcurrentDictionary < Type , string > ( ) ;
2426 private static readonly ConcurrentDictionary < Type , string > FullFriendlyTypeNames = new ConcurrentDictionary < Type , string > ( ) ;
2527
@@ -118,8 +120,8 @@ public static bool HasGenericTypeDefinition(Type type, Type genericTypeDefinitio
118120 /// <param name="inheritedType">Inherited type to be checked.</param>
119121 /// <returns>True or false.</returns>
120122 public static bool AreAssignableByGeneric ( Type baseType , Type inheritedType )
121- => IsGeneric ( inheritedType )
122- && IsGeneric ( baseType )
123+ => IsGeneric ( inheritedType )
124+ && IsGeneric ( baseType )
123125 && baseType . IsAssignableFrom ( inheritedType . GetGenericTypeDefinition ( ) ) ;
124126
125127 /// <summary>
@@ -283,27 +285,38 @@ public static T TryCreateInstance<T>(IDictionary<Type, object> constructorParame
283285 /// Gets custom attributes on the provided object.
284286 /// </summary>
285287 /// <param name="obj">Object decorated with custom attribute.</param>
288+ /// <param name="shouldInherit">Indicates whether it should include inherited component attributes</param>
286289 /// <returns>IEnumerable of objects representing the custom attributes.</returns>
287- public static IEnumerable < object > GetCustomAttributes ( object obj )
290+ public static IEnumerable < object > GetCustomAttributes ( object obj , bool shouldInherit = ShouldInheritAttributes )
288291 {
289- CacheComponentAttributes ( obj ) ;
292+ var type = obj . GetType ( ) ;
293+ var attributes = type . GetTypeInfo ( ) . GetCustomAttributes ( shouldInherit ) ;
294+ if ( attributes . Length == TypeAttributesCache . Count )
295+ {
296+ return TypeAttributesCache . Values ;
297+ }
298+
299+ CacheAttributes ( attributes , TypeAttributesCache ) ;
290300 return TypeAttributesCache . Values ;
291301 }
292302
293303 /// <summary>
294- /// Gets custom attributes including inherited ones on the provided object .
304+ /// Gets custom attributes on the provided method .
295305 /// </summary>
296- /// <param name="obj">Object decorated with custom attribute.</param>
306+ /// <param name="method">Method decorated with custom attribute.</param>
307+ /// <param name="shouldInherit">Indicates whether it should include inherited method attributes</param>
297308 /// <returns>IEnumerable of objects representing the custom attributes.</returns>
298- public static IEnumerable < object > GetCustomAttributesIncludingInherited ( object obj )
309+ public static IEnumerable < object > GetCustomAttributes ( MethodInfo method , bool shouldInherit = ShouldInheritAttributes )
299310 {
300- CacheComponentAttributes ( obj , true ) ;
301- return TypeAttributesCache . Values ;
302- }
311+ var attributes = method . GetCustomAttributes ( shouldInherit ) ;
312+ if ( attributes . Length == MethodAttributesCache . Count )
313+ {
314+ return MethodAttributesCache . Values ;
315+ }
303316
304- public static IEnumerable < object > GetCustomAttributes ( MethodInfo method )
305- => MethodAttributesCache
306- . GetOrAdd ( method , _ => method . GetCustomAttributes ( false ) ) ;
317+ CacheAttributes ( attributes , MethodAttributesCache ) ;
318+ return MethodAttributesCache . Values ;
319+ }
307320
308321 /// <summary>
309322 /// Checks whether two objects are deeply equal by reflecting all their public properties recursively. Resolves successfully value and reference types, overridden Equals method, custom == operator, IComparable, nested objects and collection properties.
@@ -398,7 +411,7 @@ public static TDelegate CreateDelegateFromMethod<TDelegate>(object instance, Fun
398411 . FirstOrDefault ( methodFilter )
399412 ? . CreateDelegate ( typeof ( TDelegate ) , instance ) as TDelegate ;
400413 }
401-
414+
402415 /// <summary>
403416 /// Checks whether property with the provided name exists in a dynamic object.
404417 /// </summary>
@@ -472,8 +485,8 @@ private static ConstructorInfo GetConstructorByUnorderedParameters(this Type typ
472485 }
473486
474487 private static bool AreDeeplyEqual (
475- object expected ,
476- object actual ,
488+ object expected ,
489+ object actual ,
477490 ConditionalWeakTable < object , object > processedElements ,
478491 DeepEqualityResult result )
479492 {
@@ -541,7 +554,7 @@ private static bool AreDeeplyEqual(
541554 ? result . Success
542555 : result . Failure ;
543556 }
544-
557+
545558 var equalsOperator = expectedType . GetMethods ( ) . FirstOrDefault ( m => m . Name == "op_Equality" ) ;
546559 if ( equalsOperator != null )
547560 {
@@ -593,15 +606,15 @@ private static bool AreDeeplyEqual(
593606 }
594607
595608 private static bool AreNotDeeplyEqual (
596- object expected ,
597- object actual ,
609+ object expected ,
610+ object actual ,
598611 ConditionalWeakTable < object , object > processedElements ,
599612 DeepEqualityResult result )
600613 => ! AreDeeplyEqual ( expected , actual , processedElements , result ) ;
601614
602615 private static bool CollectionsAreDeeplyEqual (
603- object expected ,
604- object actual ,
616+ object expected ,
617+ object actual ,
605618 ConditionalWeakTable < object , object > processedElements ,
606619 DeepEqualityResult result )
607620 {
@@ -685,8 +698,8 @@ private static bool ObjectImplementsIComparable(object obj)
685698 . FirstOrDefault ( i => i . Name . StartsWith ( "IComparable" ) ) != null ;
686699
687700 private static bool ObjectPropertiesAreDeeplyEqual (
688- object expected ,
689- object actual ,
701+ object expected ,
702+ object actual ,
690703 ConditionalWeakTable < object , object > processedElements ,
691704 DeepEqualityResult result )
692705 {
@@ -706,8 +719,8 @@ private static bool ObjectPropertiesAreDeeplyEqual(
706719 if ( expectedPropertyValue is IEnumerable && expectedPropertyValue . GetType ( ) != typeof ( string ) )
707720 {
708721 if ( ! CollectionsAreDeeplyEqual (
709- expectedPropertyValue ,
710- actualPropertyValue ,
722+ expectedPropertyValue ,
723+ actualPropertyValue ,
711724 processedElements ,
712725 result ) )
713726 {
@@ -716,8 +729,8 @@ private static bool ObjectPropertiesAreDeeplyEqual(
716729 }
717730
718731 var propertiesAreDifferent = AreNotDeeplyEqual (
719- expectedPropertyValue ,
720- actualPropertyValue ,
732+ expectedPropertyValue ,
733+ actualPropertyValue ,
721734 processedElements ,
722735 result ) ;
723736
@@ -732,16 +745,14 @@ private static bool ObjectPropertiesAreDeeplyEqual(
732745 return result . Success ;
733746 }
734747
735- private static void CacheComponentAttributes ( object obj , bool shouldInherit = false )
748+ private static void CacheAttributes ( IEnumerable < object > attributes , ConcurrentDictionary < Type , object > result )
736749 {
737- var type = obj . GetType ( ) ;
738- var attributes = type . GetTypeInfo ( ) . GetCustomAttributes ( shouldInherit ) ;
739750 foreach ( var attribute in attributes )
740751 {
741752 var attributeType = attribute . GetType ( ) ;
742- if ( ! TypeAttributesCache . ContainsKey ( attributeType ) )
753+ if ( ! result . ContainsKey ( attributeType ) )
743754 {
744- TypeAttributesCache . TryAdd ( attributeType , attribute ) ;
755+ result . TryAdd ( attributeType , attribute ) ;
745756 }
746757 }
747758 }
@@ -750,7 +761,7 @@ private static string GetFriendlyTypeName(Type type, bool useFullName)
750761 {
751762 const string anonymousTypePrefix = "<>f__" ;
752763
753- var typeName = useFullName
764+ var typeName = useFullName
754765 ? type ? . FullName ?? type ? . Name
755766 : type ? . Name ;
756767
0 commit comments