Skip to content

Commit 6f6524a

Browse files
authored
Merge pull request #715 from stakx/bugfix/case-sensitivity
Compare type member names case-sensitively
2 parents 3cd489d + 1777341 commit 6f6524a

File tree

7 files changed

+115
-11
lines changed

7 files changed

+115
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Enhancements:
1111
Bugfixes:
1212
- `InvalidProgramException` when proxying `MemoryStream` with .NET 7 (@stakx, #651)
1313
- `invocation.MethodInvocationTarget` throws `ArgumentNullException` for default interface method (@stakx, #684)
14+
- `DynamicProxyException` ("duplicate element") when type to proxy contains members whose names differ only in case (@stakx, #691)
1415

1516
Deprecations:
1617
- .NET Core 2.1, .NET Core 3.1, .NET 6, and mono tests
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright 2004-2025 Castle Project - http://www.castleproject.org/
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
namespace Castle.DynamicProxy.Tests
16+
{
17+
using System;
18+
19+
using Castle.DynamicProxy.Tests.Interceptors;
20+
21+
using NUnit.Framework;
22+
23+
[TestFixture]
24+
public class CaseSensitivityTestCase : BasePEVerifyTestCase
25+
{
26+
[TestCase(typeof(IDifferentlyCasedEvents))]
27+
[TestCase(typeof(IDifferentlyCasedMethods))]
28+
[TestCase(typeof(IDifferentlyCasedProperties))]
29+
public void Can_proxy_type_with_differently_cased_members(Type interfaceTypeToProxy)
30+
{
31+
_ = generator.CreateInterfaceProxyWithoutTarget(interfaceTypeToProxy);
32+
}
33+
34+
[Test]
35+
public void Can_distinguish_differently_cased_events_during_interception()
36+
{
37+
var interceptor = new KeepDataInterceptor();
38+
var proxy = generator.CreateInterfaceProxyWithoutTarget<IDifferentlyCasedEvents>(interceptor);
39+
40+
proxy.Abc += delegate { };
41+
Assert.AreEqual("add_Abc", interceptor.Invocation.Method.Name);
42+
43+
proxy.aBc += delegate { };
44+
Assert.AreEqual("add_aBc", interceptor.Invocation.Method.Name);
45+
46+
proxy.abC += delegate { };
47+
Assert.AreEqual("add_abC", interceptor.Invocation.Method.Name);
48+
}
49+
50+
[Test]
51+
public void Can_distinguish_differently_cased_methods_during_interception()
52+
{
53+
var interceptor = new KeepDataInterceptor();
54+
var proxy = generator.CreateInterfaceProxyWithoutTarget<IDifferentlyCasedMethods>(interceptor);
55+
56+
proxy.Abc();
57+
Assert.AreEqual("Abc", interceptor.Invocation.Method.Name);
58+
59+
proxy.aBc();
60+
Assert.AreEqual("aBc", interceptor.Invocation.Method.Name);
61+
62+
proxy.abC();
63+
Assert.AreEqual("abC", interceptor.Invocation.Method.Name);
64+
}
65+
66+
[Test]
67+
public void Can_distinguish_differently_cased_properties_during_interception()
68+
{
69+
var interceptor = new KeepDataInterceptor();
70+
var proxy = generator.CreateInterfaceProxyWithoutTarget<IDifferentlyCasedProperties>(interceptor);
71+
72+
_ = proxy.Abc;
73+
Assert.AreEqual("get_Abc", interceptor.Invocation.Method.Name);
74+
75+
_ = proxy.aBc;
76+
Assert.AreEqual("get_aBc", interceptor.Invocation.Method.Name);
77+
78+
_ = proxy.abC;
79+
Assert.AreEqual("get_abC", interceptor.Invocation.Method.Name);
80+
}
81+
82+
public interface IDifferentlyCasedEvents
83+
{
84+
event EventHandler Abc;
85+
event EventHandler aBc;
86+
event EventHandler abC;
87+
}
88+
89+
public interface IDifferentlyCasedMethods
90+
{
91+
void Abc();
92+
void aBc();
93+
void abC();
94+
}
95+
96+
public interface IDifferentlyCasedProperties
97+
{
98+
object Abc { get; }
99+
object aBc { get; }
100+
object abC { get; }
101+
}
102+
}
103+
}

src/Castle.Core/DynamicProxy/Generators/Emitters/ClassEmitter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ internal sealed class ClassEmitter
3434
private readonly List<EventEmitter> events;
3535

3636
private readonly IDictionary<string, FieldReference> fields =
37-
new Dictionary<string, FieldReference>(StringComparer.OrdinalIgnoreCase);
37+
new Dictionary<string, FieldReference>(StringComparer.Ordinal);
3838

3939
private readonly List<MethodEmitter> methods;
4040

src/Castle.Core/DynamicProxy/Generators/MetaEvent.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2004-2021 Castle Project - http://www.castleproject.org/
1+
// Copyright 2004-2025 Castle Project - http://www.castleproject.org/
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -128,7 +128,7 @@ public bool Equals(MetaEvent other)
128128
return true;
129129
}
130130

131-
if (!StringComparer.OrdinalIgnoreCase.Equals(Name, other.Name))
131+
if (!StringComparer.Ordinal.Equals(Name, other.Name))
132132
{
133133
return false;
134134
}

src/Castle.Core/DynamicProxy/Generators/MetaMethod.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2004-2021 Castle Project - http://www.castleproject.org/
1+
// Copyright 2004-2025 Castle Project - http://www.castleproject.org/
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -61,7 +61,7 @@ public bool Equals(MetaMethod other)
6161
return true;
6262
}
6363

64-
if (!StringComparer.OrdinalIgnoreCase.Equals(Name, other.Name))
64+
if (!StringComparer.Ordinal.Equals(Name, other.Name))
6565
{
6666
return false;
6767
}

src/Castle.Core/DynamicProxy/Generators/MetaProperty.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2004-2021 Castle Project - http://www.castleproject.org/
1+
// Copyright 2004-2025 Castle Project - http://www.castleproject.org/
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -159,7 +159,7 @@ public bool Equals(MetaProperty other)
159159
return true;
160160
}
161161

162-
if (!StringComparer.OrdinalIgnoreCase.Equals(Name, other.Name))
162+
if (!StringComparer.Ordinal.Equals(Name, other.Name))
163163
{
164164
return false;
165165
}

src/Castle.Core/DynamicProxy/Internal/TypeUtil.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2004-2021 Castle Project - http://www.castleproject.org/
1+
// Copyright 2004-2025 Castle Project - http://www.castleproject.org/
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
@@ -195,12 +195,12 @@ internal static bool IsFinalizer(this MethodInfo methodInfo)
195195

196196
internal static bool IsGetType(this MethodInfo methodInfo)
197197
{
198-
return methodInfo.DeclaringType == typeof(object) && string.Equals("GetType", methodInfo.Name, StringComparison.OrdinalIgnoreCase);
198+
return methodInfo.DeclaringType == typeof(object) && string.Equals("GetType", methodInfo.Name, StringComparison.Ordinal);
199199
}
200200

201201
internal static bool IsMemberwiseClone(this MethodInfo methodInfo)
202202
{
203-
return methodInfo.DeclaringType == typeof(object) && string.Equals("MemberwiseClone", methodInfo.Name, StringComparison.OrdinalIgnoreCase);
203+
return methodInfo.DeclaringType == typeof(object) && string.Equals("MemberwiseClone", methodInfo.Name, StringComparison.Ordinal);
204204
}
205205

206206
internal static void SetStaticField(this Type type, string fieldName, BindingFlags additionalFlags, object? value)
@@ -252,7 +252,7 @@ public static MemberInfo[] Sort(MemberInfo[] members)
252252
{
253253
var sortedMembers = new MemberInfo[members.Length];
254254
Array.Copy(members, sortedMembers, members.Length);
255-
Array.Sort(sortedMembers, (l, r) => string.Compare(l.Name, r.Name, StringComparison.OrdinalIgnoreCase));
255+
Array.Sort(sortedMembers, (l, r) => string.Compare(l.Name, r.Name, StringComparison.Ordinal));
256256
return sortedMembers;
257257
}
258258

0 commit comments

Comments
 (0)