System.Reflection.CustomAttributeFormatException thrown on a retrieval of derived attribute with overridden property getter
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 45/100
Research direction
Start with the minimal reproduction and inspect src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs, especially AddCustomAttributes around the cited line. Trace how overridden attribute properties and inherited setters are resolved, then add regression coverage showing that GetCustomAttributes(inherit: true) handles the getter-only override without throwing.
Written by the indexing model from the issue text.
Description
Description
Hi .Net team,
I have recently encountered a strange behavior of the derived attributes which looks like a bug to me.
On an attempt to retrieve a derived attribute that overrides just a property getter of the base attribute the system throws the System.Reflection.CustomAttributeFormatException exception (see details in the Actual Behavior section):
I didn't manage to find a similar bug, and the same time there is no compiler error about it. At the same time this looks like a bug to me. I provided more reasons in the expected behavior.
If there already exists such bug, or this is not a bug but a designed behavior, then I ask sorry for the disturbance in advance.
If this is not a proper place to report this issue, please direct me to a proper one.
Reproduction Steps
Here is the minimal code snippet:
using System;
namespace Test
{
public class BaseAttribute : Attribute
{
public virtual int P
{
get;
set;
}
}
public class DerivedAttribute : BaseAttribute
{
public override int P
{
get => 1;
}
}
[Derived(P = 2)]
public class Foo
{ }
public class Program
{
static void Main(string[] args)
{
var attr = typeof(Foo).GetCustomAttributes(inherit: true); // this line throws the exception
Console.WriteLine(attr.Length);
Console.ReadLine();
}
}
}
Notice, that if I add an empty setter to the overridden property, then the code works as expected without an error:
public class DerivedAttribute : BaseAttribute
{
public override int P
{
get => 1;
set { }
}
}
Expected behavior
The code works without throwing an error.
At first I thought that this is a problem of the C# compiler, that it doesn't display an error for this case. But for C# compiler it is perfectly fine to have only one property accessor overridden. This is discussed in this SO question: https://stackoverflow.com/questions/64595478/why-is-there-no-compiler-error-when-setting-an-overridden-property-that-is-read
Moreover, the following code works fine:
using System;
namespace Test
{
public class BaseAttribute : Attribute
{
public virtual int P
{
get;
set;
}
}
public class DerivedAttribute : BaseAttribute
{
public override int P
{
get => 1;
}
}
[Derived(P = 2)]
public class Foo
{ }
public class Program
{
static void Main(string[] args)
{
var attr = new DerivedAttribute();
attr.P = 1;
Console.WriteLine(attr.P);
Console.ReadLine();
}
}
}
So, my guess is that something is wrong with attributes.
Actual behavior
The line var attr = typeof(Foo).GetCustomAttributes(inherit: true); throws the following exception:
System.Reflection.CustomAttributeFormatException
HResult=0x80131605
Message='P' property specified was not found.
Source=System.Private.CoreLib
StackTrace:
at System.Reflection.CustomAttribute.AddCustomAttributes(ListBuilder`1& attributes, RuntimeModule decoratedModule, Int32 decoratedMetadataToken, RuntimeType attributeFilterType, Boolean mustBeInheritable, ListBuilder`1 derivedAttributes) in /_/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs:line 1272
This exception was originally thrown at this call stack:
System.Reflection.CustomAttribute.AddCustomAttributes(ref System.RuntimeType.ListBuilder<object>, System.Reflection.RuntimeModule, int, System.RuntimeType, bool, System.RuntimeType.ListBuilder<object>) in RuntimeCustomAttributeData.cs
Inner Exception 1:
NullReferenceException: Object reference not set to an instance of an object.
Regression?
On my machine this issue reproduces both for .Net Framework 4.8.1 and .Net 8.
Known Workarounds
Just add an empty property setter to the overridden property.
Configuration
Configuration:
.Net Framework 4.8.1 or .Net 8.
OS: Windows 10 Pro 64 bit, x64-based processor, Version 22H2, OS build 19045.5131
Architecture: AnyCpu
I don't know for sure, if this is specific to this configuration, but it doesn't look like this due to NRE thrown in the .Net code which can be debugged.
This isn't related to Blazor, just a simple console app.
Other information
Simple debug shows that the real error is an NRE thrown in the System.Reflection.CustomAttribute type in the AddCustomAttributes method in the following line:
https://github.com/dotnet/runtime/blob/11031c4171b5a46eedcae777fe805e7d29a94411/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs#L1583
The NRE is caused because setMethod is not checked for null before the access to its IsPublic property.
My guess is that the previous line RuntimeMethodInfo setMethod = property.GetSetMethod(true)!; obtains the property setter method only from the current type.
I believe, that a fix that just checks the setMethod for null and skips the property wouldn't make a good fix. In my example, the property has a public setter in a base type. Nothing prevents the property value from being set when the attribute is applied. In my opinion, the proper fix should support my scenario by making a lookup for the setter in the type hierarchy of the type containing the property.
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from dotnet/runtime
-
agentic-workflows untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
area-System.Reflection blocking-clean-ci-optional Known Build Error os-mac-os-x untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
area-CodeGen-coreclr untriaged
Difficulty 1/5 Under an hour Newbie friendliness 92/100
-
agentic-workflows untriaged
Difficulty 1/5 Under an hour Newbie friendliness 78/100
-
area-VM-meta-mono untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
-
:watch: Not Triaged 11.0 fundamentals/subsvc
Difficulty 2/5 1-3 hours Newbie friendliness 92/100
dotnet/AspNetCore.Docs#37699 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
SubtitleEdit/subtitleedit#15108 · 1 comment ·
-
area/docs-content Bug pulumi/docs
Difficulty 1/5 1-3 hours Newbie friendliness 94/100
-
Create parent directories only after the containment check in InstallHelper.TryExtractToDirectory Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
PowerShell/PSResourceGet#2056 ·