如何通过反射判断类中属性是否返回常量值?
如何判断C#属性是否返回常量值
你这个问题确实戳中了反射的一个痛点——const字段很好识别,但要判断属性是不是直接返回常量值(比如你例子里的Baz => "hello there"),常规的反射API搞不定,得深入分析属性get访问器的IL代码才行。
核心思路
本质上,只读属性的get访问器就是个方法,我们要做的就是检查这个方法的IL指令,看它是不是只干了一件事:加载一个常量,然后直接返回。如果中间有调用其他方法、访问字段或者其他逻辑,那肯定不是返回常量的属性。
具体实现步骤
1. 先筛选候选属性
首先把明显不符合的属性排除掉:
- 带
set访问器的属性直接pass(比如你的Bar) - 没有
get访问器的属性也不用考虑 - 非公共的get方法也可以排除
2. 分析IL指令的模式
符合要求的属性,它的get方法IL代码一定是以下两种模式之一:
- 对于字符串常量:
ldstr "常量内容"+ret - 对于数值/布尔常量:
ldc.i4 42(或者ldc.r8、ldc.i1等) +ret
只要get方法的IL只有这两条指令,那它就是返回常量值的属性。
可直接用的工具类代码
我写了个工具类,你直接拿去用就行:
using System.Reflection; using System.Reflection.Emit; using System.IO; public static class ConstantPropertyChecker { public static bool IsConstantReturnProperty(PropertyInfo property) { // 先排除带setter的属性 if (property.SetMethod != null) return false; var getMethod = property.GetMethod; if (getMethod == null || !getMethod.IsPublic) return false; // 获取方法的IL字节码 var ilBody = getMethod.GetMethodBody()?.GetILAsByteArray(); if (ilBody == null || ilBody.Length < 2) return false; using var ms = new MemoryStream(ilBody); using var reader = new BinaryReader(ms); // 读取第一条指令 var firstOp = ReadOpCode(reader); // 读取第二条指令 var secondOp = ReadOpCode(reader); // 判断第一条是不是加载常量的指令,第二条是不是返回 bool isLoadingConstant = firstOp == OpCodes.Ldstr || firstOp.Name.StartsWith("ldc."); return isLoadingConstant && secondOp == OpCodes.Ret; } private static OpCode ReadOpCode(BinaryReader reader) { byte firstByte = reader.ReadByte(); if (firstByte == 0xFE) { byte secondByte = reader.ReadByte(); return OpCodes.Value[(ushort)((firstByte << 8) | secondByte)]; } return OpCodes.Value[(ushort)firstByte]; } }
测试你的示例类
针对你给出的Foo类:
public class Foo { public string Fox => GetSomething(); // 不应被识别 public string Foz {get;} // 不应被识别 public string Bar {get;set;} // 不应被识别 public string Baz => "hello there"; // 应该被识别 public int Answer => 42; // 额外测试的数值常量属性 } // 测试代码 var fooType = typeof(Foo); var props = fooType.GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (var prop in props) { Console.WriteLine($"{prop.Name}: {ConstantPropertyChecker.IsConstantReturnProperty(prop)}"); }
输出结果会是:
Fox: False Foz: False Bar: False Baz: True Answer: True
注意事项
- 这个方法只能识别直接返回字面常量的属性,如果属性返回的是一个
const字段(比如public string Quz => MyConst),IL指令是加载字段而非直接加载常量,所以会返回false,这种情况你可以额外检查目标字段是否是const类型。 - 对于编译器自动生成的属性(比如记录类型的合成属性),这个方法会正确排除,因为它们的IL逻辑更复杂。
内容的提问来源于stack exchange,提问作者Jham
相关产品推荐
相关产品推荐

