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

8

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

假设我有一个类:

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

有没有办法做到这一点?
if (someCondition)
{
    // disable attribute. Is this possible and how can this be done?
}

为什么?也许最好在该属性上设置一个值,以便它不会执行您想要的操作。或者这是为了序列化吗? - Noon Silk
好的,你想做什么?阻止某个字段被序列化吗?还是根据某种基础使其序列化?如果是这两者之一,你总是可以有两个字段,一个带有“[NonSerialised]”,它不会被保存,当你决定它应该被保存时,你设置“其他”的那个。但我们需要更多关于你的确切原因的信息。 - Noon Silk
好的,用户可以勾选复选框来决定是否对TheProperty进行序列化。因此,根据用户的输入,属性必须发生变化。 - Natrium
嗯,我很想知道Marc会提出什么建议;不过,在那之前,序列化是如何完成的呢?是自定义XML吗?还是只依赖于二进制序列化系统? - Noon Silk
在这种情况下,我建议您可以将自己的属性放在上面,并修改一个值(doSerialise=true);我不确定这是否可行,但几乎肯定可以通过反射实现。尽管如此,我很想知道Marc Gravell是否有更好的想法。 - Noon Silk
7个回答

5

很抱歉,这是不可能的。您不能在运行时从元数据或元数据中修改属性值。

严格来说,上述说法并不正确。有一些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}())被认可并用于控制应包含哪些元素。
另一种选择是使用非默认的构造函数:
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);
    }
}

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

我在谈论在序列化时包含/排除某些属性的属性。 - Natrium
所以,只需添加一个属性NameSpecified,xmlserializer就知道是否需要序列化名称?或者我漏掉了什么? - Natrium

1

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


1

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


你能简要地解释/展示如何做到这一点吗? - RaZzLe


0

您可以实现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);
    }

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接