-
Notifications
You must be signed in to change notification settings - Fork 473
ParamArrayAttribute lost in proxy #121
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
Comments
@afscrome thanks for the detailed report. We've got some support for I've assigned this defect to v4.0 milestone. |
ProxyGenerator indeed does not "replicate" custom attributes for which AttributeUsageAttribute.Inherited is true. This check is performed in Castle.DynamicProxy.Internal.AttributeUtil.ShouldSkipAttributeReplication(Type). Can you elaborate on why this is an issue for you ? Note that if you use the following asserts, then the first will pass and the second still fails:
I'm inclined to think that's a bug in .NET. |
Thanks for looking at this @drieseng. My scenario is that I have a custom DLR language, part of which involves some custom overload resolution logic. I was using Moq in testing (which in turn uses DynamicProxy), and was getting some unexpected results because the overload resolution wasn't picking up the params array attribute ( https://github.com/afscrome/IronVelocity/blob/master/IronVelocity.Tests/Binders/SetIndexBinderTests.cs#L106 if you're interseted ). Looks like my problem is a combination of not doing the GetCustomAttribute check with inheritance, and the bugs you submitted. That said, it does look like public abstract class Base
{
public abstract string Run(params int[] args);
}
public class Sub : Base
{
public override string Run(int[] args)
{
return null;
}
} Even though the sub class doesn't have the params array explicitly attribute specified (through the
Given that the C# compiler is replicating this attribute, should dynamic proxy also replicate it? |
Yeah, I noticed that myself (and doublechecked that it still applies to the Roslyn-based compilers). To resolve your problem, a possible solution would be to only "skip replication" for custom attributes This would change line 237 to 244 of AttributeUtil.cs like this: 237 var attrs = attribute.GetCustomAttributes(typeof(AttributeUsageAttribute), true);
238
239 if (attrs.Length != 0)
240 {
241 var usage = (AttributeUsageAttribute)attrs[0];
242
243 return usage.Inherited && usage.AllowMultiple;
244 } The most important part is to continue skipping replication for custom attributes that allow to be defined multiple times. @jonorossi: |
Sorry for the delay in replying, but isn't |
If AllowMultiple is true, then redefining an attribute on a proxy would cause an "inheritable" attribute to be returned twice when invoking If however, we do not replicate an attribute if its both Inherited and AllowMultiple then we'd have the best of both worlds. Special casing Here's a test class that shows a failure in the second assert in namespace CastleTests.DynamicProxy.Tests
{
using System;
using System.Linq;
using Castle.DynamicProxy;
using NUnit.Framework;
[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
public class InheritableNotAllowMultipleMyAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
public class InheritableAllowMultipleMyAttribute : Attribute
{
}
[TestFixture]
public class ClassWithMethodWithParameterAttributes
{
private Base proxy;
[SetUp]
public void SetUp()
{
var generator = new ProxyGenerator();
proxy = generator.CreateClassProxy<Base>();
}
public abstract class Base
{
public abstract void Method([InheritableAllowMultipleMyAttribute] object a, [InheritableNotAllowMultipleMyAttribute] params int[] b);
}
[Test]
public void MethodOnProxyShouldRedefineInheritedAttributesThatDoNotAllowToBeDefinedMultipleTimes()
{
var baseType = typeof(Base);
var proxyType = proxy.GetType();
var baseMethodParamB = baseType.GetMethod("Method").GetParameters().Single(p => p.Name == "b");
var proxyMethodParamB = proxyType.GetMethod("Method").GetParameters().Single(p => p.Name == "b");
Assert.IsNotNull(Attribute.GetCustomAttribute(baseMethodParamB, typeof(InheritableNotAllowMultipleMyAttribute), false));
Assert.IsNotNull(Attribute.GetCustomAttribute(proxyMethodParamB, typeof(InheritableNotAllowMultipleMyAttribute), false));
Assert.IsNotNull(Attribute.GetCustomAttribute(baseMethodParamB, typeof(InheritableNotAllowMultipleMyAttribute), true));
Assert.IsNotNull(Attribute.GetCustomAttribute(proxyMethodParamB, typeof(InheritableNotAllowMultipleMyAttribute), true));
}
/// <summary>
/// We cannot redefine attributes that are both inheritable and allow to be defined multiple times
/// as that would could these attributes to be returned twice when invoking the <c>Attribute.GetCustomAttribute(...)</c>
/// overloads with <c>inherit</c> set to <c>true</c>.
/// </summary>
[Test]
public void MethodOnProxyShouldNotRedefineInheritedAttributesThatAllowToBeDefinedMultipleTimes()
{
var baseType = typeof(Base);
var proxyType = proxy.GetType();
var baseMethodParamA = baseType.GetMethod("Method").GetParameters().Single(p => p.Name == "a");
var proxyMethodParamA = proxyType.GetMethod("Method").GetParameters().Single(p => p.Name == "a");
Assert.IsNotNull(Attribute.GetCustomAttribute(baseMethodParamA, typeof(InheritableAllowMultipleMyAttribute), false));
Assert.IsNull(Attribute.GetCustomAttribute(proxyMethodParamA, typeof(InheritableAllowMultipleMyAttribute), false));
Assert.IsNotNull(Attribute.GetCustomAttribute(baseMethodParamA, typeof(InheritableAllowMultipleMyAttribute), true));
Assert.IsNotNull(Attribute.GetCustomAttribute(proxyMethodParamA, typeof(InheritableAllowMultipleMyAttribute), true));
}
}
} Note that there's also a bug which causes nested public attributes to never be replicated, but I'll submit a separate issue or PR for that. |
@drieseng sorry for the delay again, I kept leaving it because I think I'm still missing something. I've put together an example for what I was asking in my last comment about Why in this use of attributes would you want the [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class TheoryAttribute { }
[AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class InlineDataAttribute { }
public class ClassToProxy
{
[Theory]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
public void MyMethod(int value)
{
}
} |
Unassigning from the v4.0 milestone. |
@drieseng have you had a chance to read my last comment with some further questions? |
@drieseng, @jonorossi, this issue appears to have turned towards the general question of how attribute replication should be done in relation to an attribute's
|
@stakx I agree. See my comment from Feb 26 2016:
It became a discussion because I didn't understand how the proposed change inspecting |
Thanks everyone for reporting, discussing and fixing this defect. #270 has been merged and will be released in v4.1 later today. |
The only way to detect through reflection if a parameter is a parameter array is by checking if the parameter has the
ParamArrayAttribute
attribute defined on it. When creating a proxy object, theParamArrayAttribute
is lost on the method & indexer parameter arrays.This can be demonstrated in the following code - I'd expect both assertions to pass, but they fail.
The text was updated successfully, but these errors were encountered: