Сохраняет ли GetCustomAttributes () порядок атрибутов в .NET? - PullRequest
12 голосов
/ 26 января 2009

Название в значительной степени говорит само за себя. Когда я делаю некоторые размышления в своих классах, будет ли метод MemberInfo.GetCustomAttributes () сохранять порядок атрибутов в элементе или нет? В официальной документации ничего не сказано так или иначе.


Если вам интересно, зачем мне это нужно, вот полное объяснение. Он длинен и не нужен для вопроса, как он задается сейчас, но, возможно, кто-то может придумать альтернативное решение более серьезной проблемы, которое не предполагает использование порядка перечисления атрибутов.

Я пытаюсь создать гибкую среду для приложения (ASP.NET), которое, как ожидается, будет иметь довольно длительный срок службы. В течение курса он получит много форм, которые должны быть доступны из меню. Чтобы облегчить жизнь разработчикам, я создал MenuItemAttribute, который вы можете применить к классу формы. Этот атрибут имеет неограниченное количество строковых параметров в своем конструкторе, что позволяет разработчику указать, где именно форма будет находиться в меню. Типичным примером использования будет что-то вроде [MenuItem("Company", "Clients", "Orders")], что будет означать, что в меню должен быть пункт «Компания», под которым будет пункт «Клиенты», под которым будет пункт «Заказы», ​​который затем откроется. форма. Одна форма может иметь несколько таких атрибутов, если это необходимо - она ​​будет доступна из нескольких мест в меню.

Очевидно, что все меню создается во время выполнения путем перечисления всех классов в моих сборках и поиска этого атрибута. Однако недавно я получил запрос о том, что пункты меню должны быть отсортированы заранее. Формы, имеющие соответствующие функциональные возможности, должны находиться рядом друг с другом в меню. Обратите внимание, что это НЕ алфавитная сортировка, а предопределенный порядок, указанный разработчиками.

Тогда возникает проблема - как мне указать порядок в этих атрибутах? Поскольку один MenuItemAttribute описывает всю иерархию, спецификация заказа должна также включать номера заказов для всей (или, по крайней мере, части) иерархии. Номер заказа только для нижнего уровня иерархии недостаточен.

Я мог бы сделать еще один атрибут - MenuItemOrderHintAttribute, но тогда возникли бы проблемы со случаями, когда существует более одного MenuItemAttribute. Отсюда и оригинальный вопрос.

Я мог бы также расширить MenuItemAttribute, чтобы он брал либо два массива, либо массив пар, но тогда это сильно усложнило бы синтаксис. Последняя идея состоит в том, что я мог бы сделать строки в специальном формате, но это было бы довольно грязно, ИМХО.

<Ч />

ОК, у меня есть другая идея. Давайте использовать порядок, который предложил Джон Скит. Это позволит указать порядок для последнего уровня иерархии, а не для более высоких. Но я мог бы изменить атрибут так, чтобы он применялся не только к классам, но и к самой сборке. В этом случае пункт меню не будет иметь связанной формы. На уровне сборки эти атрибуты могут затем использоваться для указания порядка среди более высоких уровней иерархии.

Тогда это компромисс между централизованной и децентрализованной системой меню. Есть мысли, почему это будет плохой идеей?

Ответы [ 4 ]

17 голосов
/ 26 января 2009

Лексическое упорядочение элементов в файле равно абсолютно , которое никоим образом не будет сохраняться в итоговых сборках CIL и не будет соблюдаться в результатах, возвращаемых из Reflection. Этот порядок даже не гарантируется одинаковым при повторных вызовах в пределах одного домена приложения!

Обратите внимание, что MS нарушила этот порядок в других частях отражения в прошлом (отмечая, что это фактически вызвало некоторые проблемы для некоторого их кода), таким образом, даже если это работает, в настоящий момент ничего не происходит остановка этого взлома в будущем или на разных платформах.

Рассмотрите возможность изменения вашей модели атрибутов, чтобы позволить выражать семантическую информацию напрямую, а не полагаться на упорядочение.

8 голосов
/ 26 января 2009

Я бы поместил одно дополнительное (необязательное) значение в конструктор MenuItemAttribute, которое равно "order" или "priority":

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

Я не говорю, что это было бы красиво, но это эффективно позволило бы вам указать порядок.

1 голос
/ 26 января 2009

К сожалению, нет, вы не можете гарантировать, что заказ будет таким же, как заказ, в котором вы их указали.

Редактировать: обходной путь

Отказ от ответственности: я заранее прошу прощения, если я неправильно понимаю вашу главную проблему.

Звучит так, как будто вам нужно передать n количество строк в MenuItemAttribute и MenuItemAttribute поддерживать порядок этих строк, введенный разработчиком в конструкторе атрибутов.

Вот возможное решение:

Пусть MenuItemAttribute использует LinkedList<String> для поддержания своего состояния. Затем вы можете перебрать params String[] в своем конструкторе и добавить их к LinkedList<String>, который будет поддерживать порядок.

0 голосов
/ 26 января 2009

Мне кажется, что ваша проблема действительно связана с тем фактом, что формы указывают, где они появляются в вашем меню, что означает, что вы пытаетесь создать меню, комбинируя все формы в сборке. Возможно, будет проще, если вы укажете структуру меню отдельно от любых форм и разрешите формы из различных свойств / атрибутов, определенных в классе, соответствующем элементу меню.

, например

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

Затем, когда выбран пункт меню, как-нибудь разрешите форму и отобразите ее. Недостатком этого подхода является то, что любые новые формы требуют изменения кода, определяющего структуру меню вместе с самой формой, но это будет довольно незначительным ...

...