Подтвердить что ты не робот

Does GetCustomAttributes() сохраняет порядок атрибутов в .NET?

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


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

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

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

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

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

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


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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

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

Ответ 3

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

Изменить: обходной путь

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

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

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

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

Ответ 4

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

например.

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

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