使用反射按声明顺序获取属性

Mag*_*nus 73 c# reflection properties getproperties

我需要按照在类中声明它们的顺序使用反射来获取所有属性.根据MSDN,使用时无法保证订单GetProperties()

GetProperties方法不以特定顺序返回属性,例如按字母顺序或声明顺序.

但我已经读过,通过订购属性有一个解决方法MetadataToken.所以我的问题是,这样安全吗?我似乎无法在MSDN上找到有关它的任何信息.或者有没有其他方法来解决这个问题?

我目前的实施情况如下:

var props = typeof(T)
   .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
   .OrderBy(x => x.MetadataToken);
Run Code Online (Sandbox Code Playgroud)

gho*_*ord 132

在.net 4.5 (甚至vs2012中的.net 4.0)中,您可以使用带有[CallerLineNumber]属性的聪明技巧进行反射,让编译器为您的属性插入顺序:

[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
public sealed class OrderAttribute : Attribute
{
    private readonly int order_;
    public OrderAttribute([CallerLineNumber]int order = 0)
    {
        order_ = order;
    }

    public int Order { get { return order_; } }
}


public class Test
{
    //This sets order_ field to current line number
    [Order]
    public int Property2 { get; set; }

    //This sets order_ field to current line number
    [Order]
    public int Property1 { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后使用反射:

var properties = from property in typeof(Test).GetProperties()
                 where Attribute.IsDefined(property, typeof(OrderAttribute))
                 orderby ((OrderAttribute)property
                           .GetCustomAttributes(typeof(OrderAttribute), false)
                           .Single()).Order
                 select property;

foreach (var property in properties)
{
   //
}
Run Code Online (Sandbox Code Playgroud)

如果必须处理部分类,则可以使用以下方法对属性进行排序[CallerFilePath].

  • 这是有效的,除了 1 个类继承另一个类并且我需要所有属性的顺序。这也有诀窍吗? (3认同)
  • 这确实非常聪明!我想知道它是否有任何反对它的反驳论点?实际上对我来说似乎很优雅.我正在使用Linq2CSV,我想我将继承自`CsvColumnAttribute`并将其用作默认的`FieldIndex`值 (2认同)
  • @julealgon我认为反对这个问题的论点是,如果他们不理解该属性是以这种方式使用的话,那么有人可能会在重构时破坏_positional_ API.我仍然认为它非常优雅,只是说如果有人复制/粘贴这个并且正在寻找动机为下一个人留下一些评论. (2认同)
  • 关于底部“部分类”评论的小注释;对于部分类,**没有定义的顺序**;实际上,实现细节是它们按照传递给编译器的顺序到达那里,但*即使这没有在任何地方定义*作为硬规则。 (2认同)

Yah*_*hia 12

根据MSDN MetadataToken在一个模块中是独一无二的 - 没有任何说明它保证任何订单.

即使它确实按照您希望的方式运行,也可能是特定于实现的,并且可能随时更改,恕不另行通知.

请参阅此旧版MSDN博客文章.

我强烈建议远离对此类实现细节的依赖 - 请参阅Marc Gravell的回答.

如果你在编译时需要一些东西,你可以看看Roslyn(虽然它处于一个非常早期的阶段).


Chr*_*ney 12

如果你要去属性路线,这是我过去使用过的方法;

public static IOrderedEnumerable<PropertyInfo> GetSortedProperties<T>()
{
  return typeof(T)
    .GetProperties()
    .OrderBy(p => ((Order)p.GetCustomAttributes(typeof(Order), false)[0]).Order);
}
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它;

var test = new TestRecord { A = 1, B = 2, C = 3 };

foreach (var prop in GetSortedProperties<TestRecord>())
{
    Console.WriteLine(prop.GetValue(test, null));
}
Run Code Online (Sandbox Code Playgroud)

哪里;

class TestRecord
{
    [Order(1)]
    public int A { get; set; }

    [Order(2)]
    public int B { get; set; }

    [Order(3)]
    public int C { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

如果你在所有属性上没有可比属性的类型上运行它,那么该方法将是barf,所以要小心如何使用它并且它应该足以满足要求.

我已经省略了Order:Attribute的定义,因为在Yahia链接到Marc Gravell的帖子中有一个很好的样本.

  • 如果这是一个显示要求,那么使用[DataAnnotations.DisplayAttribute](http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.displayattribute.aspx)是合适的,它有一个订单领域. (2认同)

lab*_*lbe 9

另一种可能性是使用该System.ComponentModel.DataAnnotations.DisplayAttribute Order财产。由于它是内置的,因此无需创建新的特定属性。

然后选择这样的有序属性

const int defaultOrder = 10000;
var properties = type.GetProperties().OrderBy(p => p.FirstAttribute<DisplayAttribute>()?.GetOrder() ?? defaultOrder).ToArray();
Run Code Online (Sandbox Code Playgroud)

类可以这样呈现

public class Toto {
    [Display(Name = "Identifier", Order = 2)
    public int Id { get; set; }

    [Display(Name = "Description", Order = 1)
    public string Label {get; set; }
}
Run Code Online (Sandbox Code Playgroud)


Tar*_*aro 5

我测试过的 MetadataToken 排序是有效的。

这里的一些用户声称这在某种程度上不是好的方法/不可靠,但我还没有看到任何证据 - 也许当给定的方法不起作用时,您可以在这里发布一些代码片段?

关于向后兼容性 - 当您现在正在处理 .net 4 / .net 4.5 时 - Microsoft 正在制作 .net 5 或更高版本,因此您几乎可以假设这种排序方法将来不会被破坏。

当然,也许到 2017 年,当您升级到 .net9 时,您会遇到兼容性问题,但到那时,Microsoft 人员可能会弄清楚“官方排序机制”。返回或破坏事物是没有意义的。

为属性排序使用额外的属性也需要时间和实现 - 如果 MetadataToken 排序有效,为什么要打扰?