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

Получить свойства в порядке декларации с использованием отражения

Мне нужно получить все свойства, используя отражение в том порядке, в котором они объявлены в классе. Согласно MSDN, при использовании GetProperties()

порядок не может быть гарантирован,

Метод GetProperties не возвращает свойства в определенном порядок, например алфавитный или порядок объявления.

Но я прочитал, что есть способ обхода путем упорядочения свойств с помощью MetadataToken. Так что мой вопрос в том, что это безопасно? Я не могу найти информацию об MSDN. Или есть ли другой способ решения этой проблемы?

Моя текущая реализация выглядит следующим образом:

var props = typeof(T)
   .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
   .OrderBy(x => x.MetadataToken);
4b9b3361

Ответ 1

Согласно MSDN MetadataToken является уникальным внутри одного модуля - нет ничего, говоря, что он гарантирует любой заказ вообще.

ДАЖЕ, если он будет вести себя так, как вы хотите, чтобы это было связано с реализацией и могло меняться в любое время без уведомления.

Посмотрите эту старую .

Я бы настоятельно рекомендовал избегать любых зависимостей от таких деталей реализации - см. этот ответ от Марка Гравелла.

Если вам нужно что-то во время компиляции, вы можете взглянуть на Roslyn (хотя он находится на очень ранней стадии).

Ответ 2

В .net 4.5 (и даже .net 4.0 в vs2012) вы можете сделать гораздо лучше с отражением, используя умный трюк с атрибутом [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; }
}

И затем используйте отражение:

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)
{

}

Если вам приходится иметь дело с частичными классами, вы можете дополнительно отсортировать свойства с помощью [CallerFilePath].

Ответ 3

Если вы собираетесь использовать маршрут атрибута, здесь используется метод, который я использовал в прошлом;

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

Затем используйте его следующим образом:

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

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

Где

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

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

    [Order(3)]
    public int C { get; set; }
}

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

Я отказался от определения Order: Attribute, поскольку в Yahia есть хороший образец ссылки на пост Марка Гравелла.

Ответ 4

Я тестировал сортировку по MetadataToken.

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

О обратной совместимости - пока вы сейчас работаете над своим .net 4/.net 4.5 - Microsoft делает .net 5 или выше, поэтому вы в значительной степени можете предположить, что этот метод сортировки не будет нарушен в будущем.

Конечно, к 2017 году, когда вы будете обновляться до .net9, вы столкнетесь с перерывом в совместимости, но к этому времени ребята из Microsoft, вероятно, выяснят "официальный механизм сортировки". Не имеет смысла возвращаться или нарушать вещи.

Игра с дополнительными атрибутами для упорядочения свойств также требует времени и реализации - зачем беспокоиться, работает ли сортировка MetadataToken?

Ответ 5

Вы можете использовать DisplayAttribute в System.Component.DataAnnotations вместо пользовательского атрибута. В любом случае ваше требование должно что-то делать с дисплеем.

Ответ 6

Если вы довольны дополнительной зависимостью, Marc Gravell Protobuf-Net можно использовать для этого, не беспокоясь о лучшем способе для реализации рефлексии и кеширования и т.д. Просто украсьте свои поля, используя [ProtoMember], а затем получите доступ к полям в числовом порядке, используя:

MetaType metaData = ProtoBuf.Meta.RuntimeTypeModel.Default[typeof(YourTypeName)];

metaData.GetFields();