Получить свойства в порядке декларации с использованием отражения
Мне нужно получить все свойства, используя отражение в том порядке, в котором они объявлены в классе. Согласно MSDN, при использовании GetProperties()
порядок не может быть гарантирован,
Метод GetProperties не возвращает свойства в определенном порядок, например алфавитный или порядок объявления.
Но я прочитал, что есть способ обхода путем упорядочения свойств с помощью MetadataToken
. Так что мой вопрос в том, что это безопасно? Я не могу найти информацию об MSDN. Или есть ли другой способ решения этой проблемы?
Моя текущая реализация выглядит следующим образом:
var props = typeof(T)
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.OrderBy(x => x.MetadataToken);
Ответы
Ответ 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();