GetCustomAttributes()是否保留.NET中的属性顺序?

时间:2009-01-26 14:47:53

标签: .net reflection attributes enumeration

标题几乎说明了一切。当我通过我的类进行反思时,MemberInfo.GetCustomAttributes()方法是否会保留成员的属性顺序?官方文档没有说明任何方式。


如果你想知道我为什么需要这个,这里有完整的解释。它现在提出的问题很冗长而且不需要,但也许有人可以提出一个替代解决方案,解决更大的问题,而不涉及依赖属性枚举顺序。

我正在尝试为(ASP.NET)应用程序创建一个灵活的框架,该应用程序预计具有相当长的生命周期。在课程中,它将获得许多必须从菜单访问的表格。为了让开发人员的生活更轻松,我已经制作了MenuItemAttribute,您可以将其应用于表单的类。此属性在其构造函数中具有无限量的字符串参数,允许开发人员指定表单在菜单中的确切位置。一个典型的用法示例就像[MenuItem("Company", "Clients", "Orders")],这意味着菜单应该有一个项目“公司”,在该项目下将有一个项目“客户”,其下将有一个项目“订单” - 这将然后打开表格。如果需要,单个表单可以具有多个这些属性 - 然后可以从菜单中的多个位置访问它。

显然,整个菜单是在运行时通过枚举程序集中的所有类并搜索此属性来构建的。但是最近我收到了一个请求,菜单项应该以预定义的方式排序。具有相关功能的表单应该在菜单中彼此相邻。请注意,这不是按字母顺序排序,而是开发人员指定的预定义顺序。

这会带来问题 - 如何在这些属性中指定顺序?由于一个MenuItemAttribute描述了整个层次结构,因此订单规范还应包括层次结构的整个(或至少一部分)的订单号。只是层次结构较低级别的订单号是不够的。

我可以创建另一个属性 - MenuItemOrderHintAttribute,但是当有多个MenuItemAttribute时,这会带来问题。因此,最初的问题。

我还可以扩展MenuItemAttribute以获取两个数组或一对数组,但这会使语法复杂化很多。最后一个想法是,我可以使字符串具有特殊格式,但这将是相当混乱的恕我直言。


好的,我有另一个想法。让我们使用Jon Skeet建议的顺序。这将允许指定层次结构的最后一级的顺序,而不是更高级别的顺序。但我可以修改属性,使其不仅适用于类,还适用于程序集本身。在这种情况下,菜单项将没有关联的表单。在汇编级别,这些属性可用于指定层次结构的更高级别之间的排序。

这是集中式和分散式菜单系统之间的权衡。有什么想法为什么这会是一个坏主意?

4 个答案:

答案 0 :(得分:17)

文件中元素的词汇排序绝对无法保证在生成的CIL程序集中始终保留,也不会在从Reflection返回的结果中得到遵守。对于同一应用程序域中的重复调用,此顺序甚至不保证相同!

请注意,MS在过去的反射的其他部分已经破坏了这种顺序(注意到它们确实这导致了一些代码的某些问题),因此,即使它恰好在此刻没有任何工作在将来或不同的平台上停止这种打破。

考虑更改属性模型以允许直接表达语义信息,而不是依赖于排序。

答案 1 :(得分:8)

我在MenuItemAttribute构造函数中添加了一个额外的(可选)值,即“order”或“priority”:

[MenuItem(0, "Company", "Clients", "Orders")]
[MenuItem(1, "Foo", "Bar", "Baz")]

我不是说它会很漂亮,但它会有效地允许你指定顺序。

答案 2 :(得分:1)

不幸的是,不能保证订单与您指定订单的顺序相同。

编辑:解决方法

免责声明:如果我误解了你的最终问题,我会提前道歉。

听起来好像你需要能够将n个字符串传递给MenuItemAttribute并让MenuItemAttribute维护开发人员在属性构造函数中输入的这些字符串的顺序

这是一个可能的解决方案:

MenuItemAttribute使用LinkedList<String>来维持其状态。然后,您可以在构造函数中迭代params String[],并将它们添加到维护订单的LinkedList<String>

答案 3 :(得分:0)

在我看来,您的问题确实源于这样一个事实:表单指定了它们在菜单中的显示位置,这意味着您正在尝试通过组合程序集中的所有表单来构建菜单。如果您从任何表单中单独指定菜单的结构可能会更容易,并从与菜单项对应的类上定义的各种propeties / attributes中解析表单。

e.g。

public class MenuItem
{
    string Text { get; }
    Type FormType { get; }
    ICollection<MenuItem> SubItems { get; }
}

然后,当选择菜单项时,以某种方式解析表单并显示它。这种方法的缺点是,任何新表单都需要更改指定菜单结构的代码以及表单本身,但这将是非常小的......