为何GetFields(BindingFlags.NonPublic|Instance)会检测到公共事件?
问题解析与解决方案
1. 为什么GetFields(BindingFlags.NonPublic | BindingFlags.Instance)会检测到公共事件
C#里声明public event时,编译器会自动生成私有实例字段来存储事件的委托引用(除非你手动实现了事件的add/remove逻辑)。这些自动生成的字段属于非公共实例成员,刚好匹配BindingFlags.NonPublic | BindingFlags.Instance的筛选条件,所以会被你的反射代码捕获到。
比如你写的public event Action<GameEntity> TakesDamage;,编译后会生成类似private Action<GameEntity> <TakesDamage>k__BackingField;的私有字段,这个字段就是被你检测到的"公共事件"对应的底层实现。
2. 为什么field.GetCustomAttribute(typeof(PrivateIgnore))返回null
- 事件上的特性不继承给后备字段:你把
[PrivateIgnore]加在了event声明上,但反射拿到的是编译器生成的私有后备字段,这个字段并没有继承事件上标记的特性,所以调用字段的GetCustomAttribute自然返回null。 - 字段特性可能未指定适用目标:你的
PrivateIgnore特性默认只能应用在类、方法等有限目标上,需要显式声明支持字段,否则加在字段上的特性也无法被识别。
解决方案
步骤1:修正PrivateIgnore特性的适用范围
确保特性可以应用在字段和事件上:
using System; [AttributeUsage(AttributeTargets.Field | AttributeTargets.Event, AllowMultiple = false)] public class PrivateIgnore : Attribute {}
步骤2:精准过滤字段与事件后备字段
如果你需要保留未标记特性的私有字段,同时排除标记了特性的事件对应的后备字段,可以分两步处理:
using System.Reflection; // 获取所有非公共实例字段 var allPrivateFields = customType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance); // 第一步:过滤掉标记了PrivateIgnore的私有字段 var filteredFields = allPrivateFields .Where(field => field.GetCustomAttribute(typeof(PrivateIgnore)) == null) .ToList(); // 第二步:找到所有标记了PrivateIgnore的事件,移除它们的后备字段 var ignoredEvents = customType.GetEvents(BindingFlags.Public | BindingFlags.Instance) .Where(e => e.GetCustomAttribute(typeof(PrivateIgnore)) != null); foreach (var ev in ignoredEvents) { // 编译器生成的事件后备字段名称格式为<事件名>k__BackingField var backingFieldName = $"<{ev.Name}>k__BackingField"; var backingField = customType.GetField(backingFieldName, BindingFlags.NonPublic | BindingFlags.Instance); if (backingField != null) { filteredFields.Remove(backingField); } } var finalFields = filteredFields.ToArray();
简化方案:直接排除所有事件后备字段
如果你不需要保留任何事件的后备字段,也可以直接通过字段名称判断:
var Fields = customType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance) .Where(field => field.GetCustomAttribute(typeof(PrivateIgnore)) == null && !field.Name.Contains("k__BackingField")) .ToArray();
额外说明
- 公共字段不会被
BindingFlags.NonPublic | BindingFlags.Instance检测到,这是符合预期的,因为这个绑定标志仅筛选非公共成员。 - 若你手动实现了事件的
add/remove逻辑,编译器不会生成自动后备字段,这类事件也不会被你的初始代码捕获。
内容的提问来源于stack exchange,提问作者Stefan Carpeliuc
相关产品推荐
相关产品推荐

