-
Notifications
You must be signed in to change notification settings - Fork 472
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
Replicate custom attributes on constructor parameters #355
Conversation
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.
Just a couple of explanatory notes.
var info = AttributeUtil.CreateInfo<ParamArrayAttribute>(); | ||
parameter.SetCustomAttribute(info.Builder); | ||
var parameterBuilder = constructor.ConstructorBuilder.DefineParameter(offset + i, baseConstructorParams[i].Attributes, baseConstructorParams[i].Name); | ||
foreach (var attribute in baseConstructorParams[i].GetCustomAttributesToBeReproducedOnProxyCtor()) |
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.
For regular methods, we'd call baseMethodParams[i].GetNonInheritableAttributes()
, but we cannot do that here because no attributes would ever be returned by it due to the non-matching ctor signatures. We therefore need a new method that does all the work AttributeUtil.GetNonInheritableAttributes()
does, except for excluding inheritable custom attributes.
@@ -153,7 +153,7 @@ public static IEnumerable<CustomAttributeInfo> GetNonInheritableAttributes(this | |||
#else | |||
var attributeType = attribute.AttributeType; | |||
#endif | |||
if (ShouldSkipAttributeReplication(attributeType)) | |||
if (ShouldSkipAttributeReplication(attributeType, ignoreInheritance: false)) |
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.
ignoreInheritance: false
will result in the same behavior that was there before this parameter was added.
@@ -204,7 +224,7 @@ public static IEnumerable<CustomAttributeInfo> GetNonInheritableAttributes(this | |||
var attributeType = attribute.AttributeType; | |||
#endif | |||
|
|||
if (ShouldSkipAttributeReplication(attributeType)) | |||
if (ShouldSkipAttributeReplication(attributeType, ignoreInheritance)) |
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.
Could you use (parameter.Member as ConstructorInfo) != null
rather than those new methods.
Not sure why I wrote that inheritance could be the problem in the issue, because obviously constructors are not part of an class inheritance contract and must be redefined, but maybe I was thinking it could be this code excluding them because they are marked inheritable 😉.
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.
Could you please clarify which new methods you'd like to avoid and where a condition such as parameter.Member is ConstructorInfo
should go?
I'll quickly lay out how I ended up with my proposed changes:
-
Your check by itself would not be sufficient. For a ctor parameter attribute to get replicated, it must be of a public type, and it must not be in
AttributesToAvoidReplicating
. SinceShouldSkipAttributeReplication
already performs these checks and has a fitting name, I thought I'd reuse it. -
But to prevent
ShouldSkipAttributeReplication
from doing too much for ctor parameters, it needs the additional parameter to decide when to bypass checkingAttributeUsageAttribute.Inherited
. It has no access to aParameterInfo
, soparameter.Member is ConstructorInfo
cannot go in here. -
parameter.Member is ConstructorInfo
can go in the originalGetNonInheritableAttributes
and so avoid the two new forwarding methods. However, doing this makesGetNonInheritableAttributes
return inheritable attributes in some circumstances, which might seem like a bug, given its 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.
Yep, I would like to avoid the 2 new forwarding methods as they seem unnecessary. I was thinking you could determine if you are handling a constructor in GetNonInheritableAttributes
, pretty much thinking:
bool ignoreInheritance = (parameter.Member is ConstructorInfo);
Since constructors in .NET are not inherited (not that you'd want them to be), I don't see any problem returning all attributes via this named method because no constructor parameters can ever be inherited.
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.
Ah, gotcha. Makes sense now. Done.
87e9b22
to
391e8a3
Compare
4370cdc
to
befc287
Compare
This takes the unit test from castleproject#345 and applies some necessary changes: * Bring it in line with the recently changed directory and namespace structure of the test project. * The LINQ query fails with a `NullReferenceException` on .NET Core. Therefore rewrite the test such that this test bug doesn't surface. It's still the same test in spirit.
The only custom attribute that DynamicProxy currently reproduces on a proxy class' parameters is [ParamArray] (if present on the last parameter of the base class' ctor). This commit changes this so DynamicProxy replicates *all* non- inheritable custom attributes on ctor parameters. Regarding "non-inheritable": Because proxy ctors have a different signature than the proxied class' ctors, parameters can never inherit custom attributes from the base ctor. This means that inheritability of custom attributes needs to be ignored in the case of proxy ctors. Also some minor cleanups in `AttributeUtil` while we're looking at it.
befc287
to
38b1357
Compare
Squashed a little and rebased on top of current master, should be good to go. |
All good, merging. |
This is an attempt at resolving #341.
The gist of this PR is to change code in the following location where custom attribute reproduction happens (currently only for
ParamArrayAttribute
):Core/src/Castle.Core/DynamicProxy/Generators/BaseProxyGenerator.cs
Lines 240 to 249 in 980752c
This gets changed to replicate all custom attributes of all parameters.
There's a catch, however: Because constructors in the generated proxy type have different signatures (additional parameters e.g. for the interceptor array), the constructors' signatures do not match with those in the base class. This means that custom attribute inheritance would never work with these constructor parameters, which is why we need to disregard
[AttributeTargets(Inherited = true)]
and reproduce even those attributes.Can you think of any scenario(s) where replicating additional, "inheritable" custom attributes on ctors could cause negative side effects in downstream libraries?
Finally, take note that AFAIK even the updated code does not yet reproduce custom modifiers (modopts, modreqs) because that would have to happen in a different code location and because that's not what #341 is about. Eventually, we should reproduce custom modifiers as well, otherwise we might get confronted with something like #339, but for ctors.