1515namespace Castle . DynamicProxy . Tests
1616{
1717 using System ;
18+ using System . Reflection ;
1819
1920 using Castle . DynamicProxy . Tests . Interceptors ;
2021 using Castle . DynamicProxy . Tests . Interfaces ;
@@ -29,6 +30,47 @@ public class ProxyTypeCachingWithDifferentHooksTestCase : BasePEVerifyTestCase
2930#endif
3031 public class CustomHook : AllMethodsHook { }
3132
33+ #if FEATURE_SERIALIZATION
34+ [ Serializable ]
35+ #endif
36+ public class EquatableHook : IProxyGenerationHook , IEquatable < EquatableHook >
37+ {
38+ public override bool Equals ( object obj ) => Equals ( obj as EquatableHook ) ;
39+ public override int GetHashCode ( ) => GetType ( ) . GetHashCode ( ) ;
40+
41+ public bool Equals ( EquatableHook other ) => other is EquatableHook ;
42+
43+ public void MethodsInspected ( ) { }
44+ public void NonProxyableMemberNotification ( Type type , MemberInfo memberInfo ) { }
45+ public bool ShouldInterceptMethod ( Type type , MethodInfo methodInfo ) => false ;
46+ }
47+
48+ #if FEATURE_SERIALIZATION
49+ [ Serializable ]
50+ #endif
51+ public record class RecordClassHook : IProxyGenerationHook
52+ {
53+ public RecordClassHook ( string id )
54+ {
55+ Id = id ;
56+ }
57+
58+ public string Id { get ; }
59+
60+ public void MethodsInspected ( ) { }
61+ public void NonProxyableMemberNotification ( Type type , MemberInfo memberInfo ) { }
62+ public bool ShouldInterceptMethod ( Type type , MethodInfo methodInfo ) => false ;
63+ }
64+
65+ // `IEquatable<>` can lead to there being more than one `Equals` method.
66+ // This is relevant for hook classes because DynamicProxy checks whether they
67+ // override the inherited `Equals` method for proper equality semantics.
68+ [ Test ]
69+ public void Can_use_hook_that_implements_IEquatable ( )
70+ {
71+ _ = CreateProxyWithHook < EquatableHook > ( ) ;
72+ }
73+
3274 [ Test ]
3375 public void Proxies_with_same_hook_will_use_cached_proxy_type ( )
3476 {
@@ -45,9 +87,42 @@ public void Proxies_with_different_hooks_will_use_different_proxy_types()
4587 Assert . AreNotEqual ( first . GetType ( ) , second . GetType ( ) ) ;
4688 }
4789
90+ [ Test ]
91+ public void Proxies_with_different_but_equal_record_class_hooks_will_use_cached_proxy_type ( )
92+ {
93+ var firstHook = new RecordClassHook ( "1" ) ;
94+ var secondHook = new RecordClassHook ( "1" ) ;
95+ Assume . That ( firstHook , Is . Not . SameAs ( secondHook ) ) ;
96+ Assume . That ( firstHook , Is . EqualTo ( secondHook ) ) ;
97+
98+ var first = CreateProxyWithHook ( firstHook ) ;
99+ var second = CreateProxyWithHook ( secondHook ) ;
100+
101+ Assert . AreEqual ( first . GetType ( ) , second . GetType ( ) ) ;
102+ }
103+
104+ [ Test ]
105+ public void Proxies_with_different_and_unequal_record_class_hooks_will_use_different_proxy_types ( )
106+ {
107+ var firstHook = new RecordClassHook ( "1" ) ;
108+ var secondHook = new RecordClassHook ( "2" ) ;
109+ Assume . That ( firstHook , Is . Not . SameAs ( secondHook ) ) ;
110+ Assume . That ( firstHook , Is . Not . EqualTo ( secondHook ) ) ;
111+
112+ var first = CreateProxyWithHook ( firstHook ) ;
113+ var second = CreateProxyWithHook ( secondHook ) ;
114+
115+ Assert . AreNotEqual ( first . GetType ( ) , second . GetType ( ) ) ;
116+ }
117+
48118 private object CreateProxyWithHook < THook > ( ) where THook : IProxyGenerationHook , new ( )
49119 {
50- return generator . CreateInterfaceProxyWithoutTarget ( typeof ( IEmpty ) , new ProxyGenerationOptions ( new THook ( ) ) , new DoNothingInterceptor ( ) ) ;
120+ return CreateProxyWithHook ( new THook ( ) ) ;
121+ }
122+
123+ private object CreateProxyWithHook ( IProxyGenerationHook hook )
124+ {
125+ return generator . CreateInterfaceProxyWithoutTarget ( typeof ( IEmpty ) , new ProxyGenerationOptions ( hook ) , new DoNothingInterceptor ( ) ) ;
51126 }
52127 }
53128}
0 commit comments