[API Proposal]: Add `BindingFlags.IgnoreAccessModifiers`
#94.536 aperta il 8 nov 2023
Metriche repository
- Star
- (17.886 star)
- Metriche merge PR
- (Merge medio 12g 11h) (661 PR mergiate in 30 g)
Descrizione
Background and motivation
When an existing private API is made public reflection code usually breaks because it uses BindingFlags.NonPublic rather than BindingFlags.Public | BindingFlags.NonPublic, hence excluding any public members. When calling private members it's generally desirable to look for both public and non-public members.
To make this more obvious I suggest we add an explicit flag that combines the two and also add an analyzer that warns people when they only use NonPublic but not Public. Given that we already have IgnoreXxxx flag, IgnoreAccessModifiers feels natural because it also encapsulates the intent: users want to call the API, even when it's non-public.
It is true that there are several ways private reflection can fail. Sometimes, there are cases where a library has a useful non-public API that people use; it's desirable in those cases in having existing reflection users not break when those APIs are just made public, without any other changes to its name or signature.
This is different from other changes to the signature as those change how the API is being invoked (e.g. when a static method is changed into an instance method, when its renamed, or when the signature is changed). In those cases the reflection author will need to change their code because the invocation pattern has changed.
Changing an API (public or not) can be a breaking change when an API is invoked via reflection, and this proposal doesn't try to address that. However, access modifiers are a subset of the API shape that reflection code could be invariant too, if authored in a certain way. The intent here is to promote this way to reduce the cases where private reflection can be broken over time, which is especially sad in this case because making the member public expresses the intent to make it part of the promised contract. It feels counter intuitive that doing so breaks existing reflection code.
API Proposal
namespace System.Reflection;
[Flags]
public enum BindingFlags
{
// Existing:
// Public = 16,
// NonPublic = 32,
// IgnoreCase = 1,
// IgnoreReturn = 16777216,
IgnoreAccessModifiers = Public | NonPublic
}
API Usage
BindingFlags flags = BindingFlags.Static | BindingFlags.IgnoreAccessModifiers;
PropertyInfo info = type.GetProperty("SomeProperty", flags);
object value = info.GetValue(null);
This code will continue to work when SomeProperty is made public:
public static class SomeClass
{
internal static string SomeProperty { get; }
}
Alternative Designs
We could decide to no add the enum member and instead just recommend combining the existing flags.
Risks
None