【问题标题】:Is it possible to modify the attribute of a property at runtime?是否可以在运行时修改属性的属性?
【发布时间】:2015-12-04 23:56:13
【问题描述】:

是否可以在运行时修改属性的属性?

假设我有一些课程:

public class TheClass
{
    [TheAttribute]
    public int TheProperty { get; set; }
}

有没有办法做到这一点?

if (someCondition)
{
    // disable attribute. Is this possible and how can this be done?
}

【问题讨论】:

  • 为什么?也许最好在该属性上设置一个值,这样它就不会做你想做的事。或者这是为了序列化?
  • 对,那你想做什么?阻止给定字段被序列化?还是在某些基础上对其进行序列化?如果是其中任何一个,您始终可以有两个字段,一个带有“[NonSerialized]”的字段不会被保存,当您决定应该保存时,您可以设置“另一个”字段。但我们需要更多信息来了解您对此的确切理由。
  • 好的,如果用户想要TheProperty 被序列化或者不被序列化,用户可以勾选一个复选框。所以,根据用户的输入,属性必须改变。
  • 嗯。好吧,我很想看看 Marc 的建议;取而代之的是,序列化是如何完成的?它是自定义xml吗?还是你只是依赖二进制序列化系统?
  • 这样的话,我建议你可以把自己的属性放在上面,然后修改一个值(doSerialise=true);我不确定这是否可能,但几乎可以肯定应该是,通过反射。不过,我很想知道 Marc Gravell 是否有更好的主意。

标签: c# attributes


【解决方案1】:

不,这是不可能的。您不能在运行时修改元数据或一般元数据中的属性值

严格来说,上面的说法是不正确的。有一些 API 允许生成和修改一些元数据。但它们是非常特定于场景的(ENC、分析、调试),不应在通用程序中使用。

【讨论】:

    【解决方案2】:

    视情况而定;从反思的角度来看:没有。你不能。但是,如果您在谈论 System.ComponentModel 在数据绑定之类的事情中使用的属性,您可以使用TypeDescriptor.AddAttributes 来附加额外的属性。或其他涉及自定义描述符的客户模型。所以这取决于用例。


    在 xml 序列化的情况下,它变得更加有趣。首先,我们可以使用有趣的对象模型:

    using System;
    using System.Xml.Serialization;
    public class MyData
    {
        [XmlAttribute]
        public int Id { get; set; }
        [XmlAttribute]
        public string Name { get; set; }
        [XmlIgnore]
        public bool NameSpecified { get; set; }
    
        static void Main()
        {
            var ser = new XmlSerializer(typeof(MyData));
    
            var obj1 = new MyData { Id = 1, Name = "Fred", NameSpecified = true };
            ser.Serialize(Console.Out, obj1);
            Console.WriteLine();
            Console.WriteLine();
            var obj2 = new MyData { Id = 2, Name = "Fred", NameSpecified = false };
            ser.Serialize(Console.Out, obj2);
        }
    }
    

    bool {name}Specified {get;set;} 模式(连同bool ShouldSerialize{name}())被识别并用于控制要包含的元素。

    另一种选择是使用非默认 ctor:

    using System;
    using System.Xml.Serialization;
    public class MyData
    {
        [XmlAttribute]
        public int Id { get; set; }
        public string Name { get; set; }
    
        static void Main()
        {
            var obj = new MyData { Id = 1, Name = "Fred" };
    
            XmlAttributeOverrides config1 = new XmlAttributeOverrides();
            config1.Add(typeof(MyData),"Name",
                new XmlAttributes { XmlIgnore = true});
            var ser1 = new XmlSerializer(typeof(MyData),config1); 
            ser1.Serialize(Console.Out, obj);
            Console.WriteLine();
            Console.WriteLine();
            XmlAttributeOverrides config2 = new XmlAttributeOverrides();
            config2.Add(typeof(MyData), "Name",
                new XmlAttributes { XmlIgnore = false });
            var ser2 = new XmlSerializer(typeof(MyData), config2);
            ser2.Serialize(Console.Out, obj);
        }
    }
    

    请注意,如果您使用第二种方法,则需要缓存序列化程序实例,因为每次执行此操作时它都会发出一个程序集。我发现第一种方法更简单......

    【讨论】:

    • 我说的是序列化时包含/排除某些属性的属性。
    • 所以,只需添加属性NameSpecified xmlserializer 就知道他是否需要序列化 ​​Name ?还是我错过了什么?
    【解决方案3】:

    属性在编译时被烘焙到代码中。在运行时定义新属性的唯一方法是在运行时生成新代码(例如,使用 Reflection.Emit)。但是你不能改变现有代码的属性。

    【讨论】:

      【解决方案4】:

      您可以在类中放置布尔变量来禁用/启用属性,而不是在运行时禁用它。

      【讨论】:

        【解决方案5】:
        【解决方案6】:

        听起来你想考虑实现IXmlSerializable

        【讨论】:

          【解决方案7】:

          你可以实现IDataErrorInfo,然后在Validate方法中检查范围。

              public string this[string property] {
                  get { return Validate(property); }
              }
          
              public string Error { get; }
          
              protected virtual string Validate(string property) {
                  var propertyInfo = this.GetType().GetProperty(property);
                  var results = new List<ValidationResult>();
          
                  var result = Validator.TryValidateProperty(
                                            propertyInfo.GetValue(this, null),
                                            new ValidationContext(this, null, null) {
                                                MemberName = property
                                            },
                                            results);
          
                  if (!result) {
                      var validationResult = results.First();
                      return validationResult.ErrorMessage;
                  }
          
                  return string.Empty;
              }
          

          在子类中

          protected override string Validate(string property) {
                  Debug.WriteLine(property);
                  if (property == nameof(YourProperty)) {
                      if (_property > 5) {
                          return "_property out of range";
                      }
                  }
                  return base.Validate(property);
              }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-10-28
            • 2011-06-16
            • 1970-01-01
            • 1970-01-01
            • 2021-07-29
            • 1970-01-01
            • 1970-01-01
            • 2016-11-04
            相关资源
            最近更新 更多