dotnet/runtime

[API Proposal]: Add `BindingFlags.IgnoreAccessModifiers`

Open

#94,536 建立於 2023年11月8日

在 GitHub 查看
 (27 留言) (30 反應) (0 負責人)C# (5,445 fork)batch import
api-approvedarea-System.Reflectioncode-analyzercode-fixerhelp wanted

倉庫指標

Star
 (17,886 star)
PR 合併指標
 (平均合併 12天 11小時) (30 天內合併 661 個 PR)

描述

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

貢獻者指南