如何在运行时为类新实例的方法设置属性值?
首先,帮你梳理下当前代码里的几个核心问题:
你修改的是类型的方法属性,而非单个实例
TypeDescriptor.AddAttributes(typeof(AbstractClass).GetMethod("MyMethod"), ...)这段代码是给AbstractClass这个类型的MyMethod添加属性,不是针对当前创建的实例。这意味着只要创建过一个实例,所有后续AbstractClass子类实例的MyMethod都会带上这个属性,而且属性值会是第一个实例构造时的DynamicAttributeValue(甚至可能是null,因为还有下一个问题)。基类构造执行时,子类重写属性尚未初始化
C#的构造执行顺序是先跑基类构造,再初始化子类成员。所以在AbstractClass的构造函数里调用DynamicAttributeValue时,子类MyConcreteClass的重写属性还没返回"MyRunTimeValue",此时拿到的是string的默认值null。ConditionalAttribute是编译时属性,运行时添加无效
这个属性的作用是告诉编译器:如果指定符号未定义,就删除调用该方法的代码。它的逻辑完全在编译阶段处理,运行时动态添加对程序行为没有任何影响。
方案一:实例级动态关联方法属性(推荐)
如果你的需求是在运行时针对每个实例,给它的方法关联自定义属性(后续反射可用),不用修改类型元数据,自己维护一个实例级的属性存储即可:
public abstract class AbstractClass { // 实例级字典:存储方法名 -> 关联属性列表 private Dictionary<string, List<Attribute>> _methodAttributes = new Dictionary<string, List<Attribute>>(); public abstract string DynamicAttributeValue { get; } public AbstractClass() { // 给当前实例的MyMethod添加自定义属性 var customAttr = new MyRuntimeConditionalAttribute(DynamicAttributeValue); if (!_methodAttributes.ContainsKey("MyMethod")) { _methodAttributes["MyMethod"] = new List<Attribute>(); } _methodAttributes["MyMethod"].Add(customAttr); } public void MyMethod() { } // 提供方法获取当前实例的方法属性 public IEnumerable<Attribute> GetInstanceMethodAttributes(string methodName) { if (_methodAttributes.TryGetValue(methodName, out var attrs)) { return attrs; } return Enumerable.Empty<Attribute>(); } } // 自定义运行时可用的Conditional替代属性 public class MyRuntimeConditionalAttribute : Attribute { public string Condition { get; } public MyRuntimeConditionalAttribute(string condition) { Condition = condition; } } class MyConcreteClass : AbstractClass { public override string DynamicAttributeValue => "MyRunTimeValue"; static void Main(string[] args) { MyConcreteClass myClass = new MyConcreteClass(); string callingMethodName = "MyMethod"; // 从实例的自定义存储中获取属性 var attributes = myClass.GetInstanceMethodAttributes(callingMethodName); foreach (var attr in attributes) { if (attr is MyRuntimeConditionalAttribute condAttr) { Console.WriteLine(condAttr.Condition); // 输出 MyRunTimeValue } } } }
方案二:修改类型级方法元数据(谨慎使用)
如果你确实需要修改类型的方法属性(注意:这会影响该类型的所有实例),可以把添加属性的逻辑放到子类构造里,确保子类属性初始化完成后再执行:
public abstract class AbstractClass { public abstract string DynamicAttributeValue { get; } public void MyMethod() { } } class MyConcreteClass : AbstractClass { public override string DynamicAttributeValue => "MyRunTimeValue"; public MyConcreteClass() { // 子类属性已初始化,能拿到正确的DynamicAttributeValue // 注意:这会修改MyConcreteClass类型的MyMethod属性,所有实例都会受影响 TypeDescriptor.AddAttributes(typeof(MyConcreteClass).GetMethod("MyMethod"), new Attribute[] { new MyRuntimeConditionalAttribute(DynamicAttributeValue) }); } static void Main(string[] args) { MyConcreteClass myClass = new MyConcreteClass(); string callingMethodName = "MyMethod"; MethodInfo methodInfo = myClass.GetType().GetMethod(callingMethodName); // 要用TypeDescriptor读取动态添加的属性,而非GetCustomAttributes(仅读取编译时元数据) var attributes = TypeDescriptor.GetAttributes(methodInfo).Cast<Attribute>(); foreach (var attr in attributes) { if (attr is MyRuntimeConditionalAttribute condAttr) { Console.WriteLine(condAttr.Condition); // 输出 MyRunTimeValue } } } }
内容的提问来源于stack exchange,提问作者TomSelleck

