17
17
/// </summary>
18
18
public static class Reflection
19
19
{
20
+ private const bool ShouldInheritAttributes = false ;
21
+
20
22
private static readonly ConcurrentDictionary < Type , ConstructorInfo > TypesWithOneConstructorCache = new ConcurrentDictionary < Type , ConstructorInfo > ( ) ;
21
23
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 > ( ) ;
23
25
private static readonly ConcurrentDictionary < Type , string > FriendlyTypeNames = new ConcurrentDictionary < Type , string > ( ) ;
24
26
private static readonly ConcurrentDictionary < Type , string > FullFriendlyTypeNames = new ConcurrentDictionary < Type , string > ( ) ;
25
27
@@ -118,8 +120,8 @@ public static bool HasGenericTypeDefinition(Type type, Type genericTypeDefinitio
118
120
/// <param name="inheritedType">Inherited type to be checked.</param>
119
121
/// <returns>True or false.</returns>
120
122
public static bool AreAssignableByGeneric ( Type baseType , Type inheritedType )
121
- => IsGeneric ( inheritedType )
122
- && IsGeneric ( baseType )
123
+ => IsGeneric ( inheritedType )
124
+ && IsGeneric ( baseType )
123
125
&& baseType . IsAssignableFrom ( inheritedType . GetGenericTypeDefinition ( ) ) ;
124
126
125
127
/// <summary>
@@ -283,27 +285,38 @@ public static T TryCreateInstance<T>(IDictionary<Type, object> constructorParame
283
285
/// Gets custom attributes on the provided object.
284
286
/// </summary>
285
287
/// <param name="obj">Object decorated with custom attribute.</param>
288
+ /// <param name="shouldInherit">Indicates whether it should include inherited component attributes</param>
286
289
/// <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 )
288
291
{
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 ) ;
290
300
return TypeAttributesCache . Values ;
291
301
}
292
302
293
303
/// <summary>
294
- /// Gets custom attributes including inherited ones on the provided object .
304
+ /// Gets custom attributes on the provided method .
295
305
/// </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>
297
308
/// <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 )
299
310
{
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
+ }
303
316
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
+ }
307
320
308
321
/// <summary>
309
322
/// 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
398
411
. FirstOrDefault ( methodFilter )
399
412
? . CreateDelegate ( typeof ( TDelegate ) , instance ) as TDelegate ;
400
413
}
401
-
414
+
402
415
/// <summary>
403
416
/// Checks whether property with the provided name exists in a dynamic object.
404
417
/// </summary>
@@ -472,8 +485,8 @@ private static ConstructorInfo GetConstructorByUnorderedParameters(this Type typ
472
485
}
473
486
474
487
private static bool AreDeeplyEqual (
475
- object expected ,
476
- object actual ,
488
+ object expected ,
489
+ object actual ,
477
490
ConditionalWeakTable < object , object > processedElements ,
478
491
DeepEqualityResult result )
479
492
{
@@ -541,7 +554,7 @@ private static bool AreDeeplyEqual(
541
554
? result . Success
542
555
: result . Failure ;
543
556
}
544
-
557
+
545
558
var equalsOperator = expectedType . GetMethods ( ) . FirstOrDefault ( m => m . Name == "op_Equality" ) ;
546
559
if ( equalsOperator != null )
547
560
{
@@ -593,15 +606,15 @@ private static bool AreDeeplyEqual(
593
606
}
594
607
595
608
private static bool AreNotDeeplyEqual (
596
- object expected ,
597
- object actual ,
609
+ object expected ,
610
+ object actual ,
598
611
ConditionalWeakTable < object , object > processedElements ,
599
612
DeepEqualityResult result )
600
613
=> ! AreDeeplyEqual ( expected , actual , processedElements , result ) ;
601
614
602
615
private static bool CollectionsAreDeeplyEqual (
603
- object expected ,
604
- object actual ,
616
+ object expected ,
617
+ object actual ,
605
618
ConditionalWeakTable < object , object > processedElements ,
606
619
DeepEqualityResult result )
607
620
{
@@ -685,8 +698,8 @@ private static bool ObjectImplementsIComparable(object obj)
685
698
. FirstOrDefault ( i => i . Name . StartsWith ( "IComparable" ) ) != null ;
686
699
687
700
private static bool ObjectPropertiesAreDeeplyEqual (
688
- object expected ,
689
- object actual ,
701
+ object expected ,
702
+ object actual ,
690
703
ConditionalWeakTable < object , object > processedElements ,
691
704
DeepEqualityResult result )
692
705
{
@@ -706,8 +719,8 @@ private static bool ObjectPropertiesAreDeeplyEqual(
706
719
if ( expectedPropertyValue is IEnumerable && expectedPropertyValue . GetType ( ) != typeof ( string ) )
707
720
{
708
721
if ( ! CollectionsAreDeeplyEqual (
709
- expectedPropertyValue ,
710
- actualPropertyValue ,
722
+ expectedPropertyValue ,
723
+ actualPropertyValue ,
711
724
processedElements ,
712
725
result ) )
713
726
{
@@ -716,8 +729,8 @@ private static bool ObjectPropertiesAreDeeplyEqual(
716
729
}
717
730
718
731
var propertiesAreDifferent = AreNotDeeplyEqual (
719
- expectedPropertyValue ,
720
- actualPropertyValue ,
732
+ expectedPropertyValue ,
733
+ actualPropertyValue ,
721
734
processedElements ,
722
735
result ) ;
723
736
@@ -732,16 +745,14 @@ private static bool ObjectPropertiesAreDeeplyEqual(
732
745
return result . Success ;
733
746
}
734
747
735
- private static void CacheComponentAttributes ( object obj , bool shouldInherit = false )
748
+ private static void CacheAttributes ( IEnumerable < object > attributes , ConcurrentDictionary < Type , object > result )
736
749
{
737
- var type = obj . GetType ( ) ;
738
- var attributes = type . GetTypeInfo ( ) . GetCustomAttributes ( shouldInherit ) ;
739
750
foreach ( var attribute in attributes )
740
751
{
741
752
var attributeType = attribute . GetType ( ) ;
742
- if ( ! TypeAttributesCache . ContainsKey ( attributeType ) )
753
+ if ( ! result . ContainsKey ( attributeType ) )
743
754
{
744
- TypeAttributesCache . TryAdd ( attributeType , attribute ) ;
755
+ result . TryAdd ( attributeType , attribute ) ;
745
756
}
746
757
}
747
758
}
@@ -750,7 +761,7 @@ private static string GetFriendlyTypeName(Type type, bool useFullName)
750
761
{
751
762
const string anonymousTypePrefix = "<>f__" ;
752
763
753
- var typeName = useFullName
764
+ var typeName = useFullName
754
765
? type ? . FullName ?? type ? . Name
755
766
: type ? . Name ;
756
767
0 commit comments