-
Notifications
You must be signed in to change notification settings - Fork 483
Include DeclaringType.Name for nested types when generating explicit implementation names #692
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| // Copyright 2004-2021 Castle Project - http://www.castleproject.org/ | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| namespace Castle.DynamicProxy.Tests | ||
| { | ||
| using System; | ||
| using System.Reflection; | ||
|
|
||
| using NUnit.Framework; | ||
|
|
||
| using INestedSharedNameFromA = Interfaces.OuterWrapper.InnerWrapperA.ISharedName; | ||
| using INestedSharedNameFromB = Interfaces.OuterWrapper.InnerWrapperB.ISharedName; | ||
| using INestedSharedNameFromC = Interfaces.OuterWrapper.InnerWrapperC.ISharedName; | ||
|
|
||
| [TestFixture] | ||
| public class ExplicitlyImplementedNestedMethodNamesTestCase | ||
| { | ||
| [Test] | ||
| public void DynamicProxy_includes_namespace_and_declaring_type_and_type_name_in_names_of_explicitly_implemented_methods() | ||
| { | ||
| var a = typeof(INestedSharedNameFromA); | ||
| var b = typeof(INestedSharedNameFromB); | ||
| var c = typeof(INestedSharedNameFromC); | ||
|
|
||
| var proxy = new ProxyGenerator().CreateInterfaceProxyWithoutTarget( | ||
| interfaceToProxy: a, | ||
| additionalInterfacesToProxy: new[] { b, c }, | ||
| interceptors: new StandardInterceptor()); | ||
|
|
||
| var implementingType = proxy.GetType(); | ||
|
|
||
| AssertNamingSchemeOfExplicitlyImplementedMethods(b, c, implementingType); | ||
| } | ||
|
|
||
| private void AssertNamingSchemeOfExplicitlyImplementedMethods(Type b, Type c, Type implementingType) | ||
| { | ||
| const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; | ||
|
|
||
| // The assertions at the end of this method only make sense if certain preconditions | ||
| // are met. We verify those using NUnit assumptions: | ||
|
|
||
| // We require two interface types that have the same name and a method named `M` each: | ||
| Assume.That(b.IsInterface); | ||
| Assume.That(c.IsInterface); | ||
| Assume.That(b.Name == c.Name); | ||
| Assume.That(b.GetMethod("M") != null); | ||
| Assume.That(c.GetMethod("M") != null); | ||
|
|
||
| // We also need a type that implements the above interfaces: | ||
| Assume.That(b.IsAssignableFrom(implementingType)); | ||
| Assume.That(c.IsAssignableFrom(implementingType)); | ||
|
|
||
| // If all of the above conditions are met, we expect the methods from the interfaces | ||
| // to be implemented explicitly. For our purposes, this means that they follow the | ||
| // naming scheme `<namespace>.<parent types>.<type>.M`: | ||
| Assert.NotNull(implementingType.GetMethod($"{b.Namespace}.{b.DeclaringType.DeclaringType.Name}.{b.DeclaringType.Name}.{b.Name}.M", bindingFlags)); | ||
| Assert.NotNull(implementingType.GetMethod($"{c.Namespace}.{b.DeclaringType.DeclaringType.Name}.{b.DeclaringType.Name}.{c.Name}.M", bindingFlags)); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| // Copyright 2004-2021 Castle Project - http://www.castleproject.org/ | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| namespace Castle.DynamicProxy.Tests.Interfaces | ||
| { | ||
| public static class OuterWrapper | ||
| { | ||
| public static class InnerWrapperA | ||
| { | ||
| public interface ISharedName | ||
| { | ||
| void M(); | ||
| } | ||
| } | ||
|
|
||
| public static class InnerWrapperB | ||
| { | ||
| public interface ISharedName | ||
| { | ||
| void M(); | ||
| } | ||
| } | ||
|
|
||
| public static class InnerWrapperC | ||
| { | ||
| public interface ISharedName | ||
| { | ||
| void M(); | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -62,23 +62,30 @@ protected void SwitchToExplicitImplementationName() | |
| nameBuilder.Append(ns); | ||
| nameBuilder.Append('.'); | ||
| } | ||
| AppendTypeName(nameBuilder, sourceType); | ||
| AppendTypeName(nameBuilder, sourceType, true); | ||
| nameBuilder.Append('.'); | ||
| nameBuilder.Append(name); | ||
| this.name = nameBuilder.ToString(); | ||
| } | ||
| else if (ns != null) | ||
| { | ||
| this.name = string.Concat(ns, ".", sourceType.Name, ".", name); | ||
| this.name = string.Concat(ns, ".", GetFullTypeName(sourceType), ".", name); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I remember correctly, the pre-existing code in Your code change introduces So if we go with your change, that whole method could perhaps be simplified to a single case where a If we go with the existing code's structure, you'd have to find a way of not relying on I think I'd favor simplifying the code by rewriting the |
||
| } | ||
| else | ||
| { | ||
| this.name = string.Concat(sourceType.Name, ".", name); | ||
| this.name = string.Concat(GetFullTypeName(sourceType), ".", name); | ||
| } | ||
|
|
||
| static void AppendTypeName(StringBuilder nameBuilder, Type type) | ||
| static void AppendTypeName(StringBuilder nameBuilder, Type type, bool fullName = false) | ||
| { | ||
| if (fullName) | ||
| { | ||
| AppendFullTypeName(nameBuilder, type); | ||
| return; | ||
| } | ||
|
|
||
| nameBuilder.Append(type.Name); | ||
|
|
||
| if (type.IsGenericType) | ||
| { | ||
| nameBuilder.Append('['); | ||
|
|
@@ -94,6 +101,31 @@ static void AppendTypeName(StringBuilder nameBuilder, Type type) | |
| nameBuilder.Append(']'); | ||
| } | ||
| } | ||
|
|
||
| static string GetFullTypeName(Type type) | ||
| { | ||
| var nameBuilder = new StringBuilder(); | ||
| AppendFullTypeName(nameBuilder, type); | ||
| return nameBuilder.ToString(); | ||
| } | ||
|
|
||
| static void AppendFullTypeName(StringBuilder nameBuilder, Type type) | ||
| { | ||
| if (type.IsNested) | ||
| { | ||
| AppendFullTypeName(nameBuilder, type.DeclaringType); | ||
| nameBuilder.Append('.'); | ||
| } | ||
|
|
||
| if (type.IsGenericType) | ||
| { | ||
| AppendTypeName(nameBuilder, type); | ||
| } | ||
| else | ||
| { | ||
| nameBuilder.Append(type.Name); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not simply call
AppendFullTypeNamedirectly instead of going throughAppendTypeNameusing that new parameter?