C# 如何以编程方式更新DisplayAttribute上的Order属性?

C# 如何以编程方式更新DisplayAttribute上的Order属性?,c#,reflection,C#,Reflection,给定以下代码: using System.ComponentModel.DataAnnotations; using System.Linq; using Xunit; namespace Company.Tests { public class MyObject { [Display(Order = 1000)] public virtual string StringPropertyB { get; set; } [Disp

给定以下代码:

using System.ComponentModel.DataAnnotations;
using System.Linq;
using Xunit;

namespace Company.Tests
{
    public class MyObject
    {
        [Display(Order = 1000)]
        public virtual string StringPropertyB { get; set; }

        [Display(Order = 2000)]
        public virtual string StringPropertyA { get; set; }
    }

    public class MyObjectTest
    {
        [Fact]
        public void X()
        {
            var properties = typeof(MyObject).GetProperties();
            var stringPropertyBPropertyInfo = properties[0];
            var stringPropertyAPropertyInfo = properties[1];

            // Debugger Display = "{[System.ComponentModel.DataAnnotations.DisplayAttribute(Order = 1000)]}"
            var bDisplayAttribute = stringPropertyBPropertyInfo.GetCustomAttributesData().FirstOrDefault();

            // Debugger Display = "{[System.ComponentModel.DataAnnotations.DisplayAttribute(Order = 2000)]}"
            var aDisplayAttribute = stringPropertyAPropertyInfo.GetCustomAttributesData().FirstOrDefault();
        }
    }
}
如何以编程方式更新Order属性


我想在它被设置为新值后更新它。(用例不必指定顺序,而是自动为顺序指定一个值,以匹配MyObject上的属性从上到下的显示顺序。)

而不是您所做的

public class MyObject
{
    [DefaultValue(1000)]
    public virtual int StringPropertyBOrder { get; set; }
    public virtual string StringPropertyB { get; set; }

    [DefaultValue(2000)]
    public virtual int StringPropertyAOrder { get; set; }
    public virtual string StringPropertyA { get; set; }
}

更新答案: 因为属性是“静态元数据”,所以在编译之后不能持久地更改它们。()

因此,实现OP对属性值进行持久更改的请求的一种方法是在运行时编译代码。如果这是你所需要的,也许值得认真考虑用不同的方法来解决你试图解决的问题,因为这意味着你要做更多的工作(但对我来说这太酷了)

也有可能使用名为TypeBuilder()的东西。我对此并不熟悉,但它看起来很有希望。它看起来也像是使用运行时编译

运行时编译:

这里有一种方法可以实现这一点,即在运行时编译代码。(使用NUnit,而不是像OP那样使用XUnit):

关于C#运行时编译的一个很好的提升指南(这是我的一点热情):

原始答案:

从技术上讲,它表明您确实可以更改属性的值,但正如OP所指出的,这不会持续

using System.ComponentModel.DataAnnotations;
using System.Linq;
using Xunit;

namespace Company.Tests
{
    public class MyObject
    {
        [Display(Order = 1000)]
        public virtual string StringPropertyB { get; set; }

        [Display(Order = 2000)]
        public virtual string StringPropertyA { get; set; }
    }

    public class MyObjectTest
    {
        [Fact]
        public void X()
        {
            var properties = typeof(MyObject).GetProperties();
            var stringPropertyBPropertyInfo = properties[0];
            var bDisplayAttribute = (DisplayAttribute)stringPropertyBPropertyInfo.GetCustomAttributes().First();
            var props = bDisplayAttribute.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public).ToList();
            props.Single(p => p.Name == "Order").SetValue(bDisplayAttribute, 5);

        }
    }
}

正如斯基特先生在回答中所说,桑珀指出,就我所知,这是不可能做到的。您可以更新订单值,但它不会持续:

using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using Shouldly;
using Xunit;

namespace Tests
{
    public class ObjectWithDisplayOrder
    {
        [Display(Order = 0)]
        public virtual string StringPropertyB { get; set; }

        [Display(Order = 0)]
        public virtual string StringPropertyA { get; set; }
    }

    public class DisplayOrderTests
    {
        [Fact]
        public void ShouldUpdateDisplayOrderProperty()
        {
            const int updatedOrderValue = 1000;

            var properties = typeof(ObjectWithDisplayOrder).GetProperties();
            foreach (var property in properties)
            {
                var displayAttribute = (DisplayAttribute) property.GetCustomAttributes().First(a => a is DisplayAttribute);
                var props = displayAttribute.GetType().GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public).ToList();
                props.Single(p => p.Name == "Order").SetValue(displayAttribute, updatedOrderValue);
                // displayAttribute Order is 1000 here, but it's not persisted...
            }

            foreach (var property in properties)
            {
                var displayAttribute = (DisplayAttribute) property.GetCustomAttributes().First(a => a is DisplayAttribute);
                displayAttribute.GetOrder().ShouldBe(updatedOrderValue); // Fails - Order is still 0
            }
        }
    }
}

如果您需要更改它,我想您可能只想将其存储在属性中。我可能错了,但在我看来,在运行时修改属性将打破范式。如果我是你,我会用另一种方式重新安排你的财产。但我离你太近了……:)我一直在努力<代码>变量属性=cad.AttributeType.GetProperty(“订单”);属性设置值(whatToUseHere,30000)就是不知道使用什么来做什么这可能会帮你找到正确的方向:我还知道你可以在运行时使用反射来访问属性:public static object GetAttribute(object parent,Type attributeType){var property=parent.GetType().GetProperties().Where(prop=>Attribute.IsDefined(prop,attributeType)).Single();var attribute=property.GetCustomAttribute(attributeType);return attribute;}此外,您还可以检查对象是否具有私有“set”值…如果我今天晚些时候有时间,而且没有其他人知道,我会把一些东西放在一起,看看我是否能帮你把它放进口袋。但是他如何让
StringPropertyBOrder
做他用
[Order]
做的同样的事情呢?不管是什么,我都需要使用DisplayAttribute(它是密封的…)因为这就是我的底层框架所使用的。谢谢你,但我无法让这些值持久化,请参阅我的答案。但是“A”表示努力!:)我决心让这一切都能成功。我会看看我能对你的答案做些什么(你应该把它作为你帖子的更新发布)@vsdev-检查一下。从技术上讲,它回答了你的问题——但这意味着你还有很多工作要做。也许有必要研究一种不同的方法来实现这一点-您需要使用“显示”的用例是什么?谢谢,我使用的框架动态扫描DisplayAttribute,并根据该属性的顺序在UI中设置顺序。所以我不能使用运行时编译或类似的东西。手动设置Order属性以确保排序顺序正确并没有那么麻烦。我只是在寻找一种设置默认值的方法。谢谢,我会接受你的回答。:)隐马尔可夫模型。。。如果是动态扫描,您应该能够在运行时编译。另外,框架是否在您的控制之下?因为如果是的话,您肯定可以查看CsvReader使用的模型-它允许在运行时装饰属性。您可以使用mapping+设置框架来支持它。
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using Shouldly;
using Xunit;

namespace Tests
{
    public class ObjectWithDisplayOrder
    {
        [Display(Order = 0)]
        public virtual string StringPropertyB { get; set; }

        [Display(Order = 0)]
        public virtual string StringPropertyA { get; set; }
    }

    public class DisplayOrderTests
    {
        [Fact]
        public void ShouldUpdateDisplayOrderProperty()
        {
            const int updatedOrderValue = 1000;

            var properties = typeof(ObjectWithDisplayOrder).GetProperties();
            foreach (var property in properties)
            {
                var displayAttribute = (DisplayAttribute) property.GetCustomAttributes().First(a => a is DisplayAttribute);
                var props = displayAttribute.GetType().GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public).ToList();
                props.Single(p => p.Name == "Order").SetValue(displayAttribute, updatedOrderValue);
                // displayAttribute Order is 1000 here, but it's not persisted...
            }

            foreach (var property in properties)
            {
                var displayAttribute = (DisplayAttribute) property.GetCustomAttributes().First(a => a is DisplayAttribute);
                displayAttribute.GetOrder().ShouldBe(updatedOrderValue); // Fails - Order is still 0
            }
        }
    }
}