我需要按照在类中声明它们的顺序使用反射来获取所有属性。根据MSDN,使用GetProperties()
GetProperties方法不返回特定属性 订单,如字母或声明顺序。
但我已经读过通过MetadataToken
对属性进行排序的解决方法。所以我的问题是,这样安全吗?我似乎无法在MSDN上找到有关它的任何信息。或者还有其他方法可以解决这个问题吗?
我目前的实施情况如下:
var props = typeof(T)
.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.OrderBy(x => x.MetadataToken);
答案 0 :(得分:122)
在.net 4.5 (and even .net 4.0 in 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]
对属性进行排序。
答案 1 :(得分:12)
根据MSDN MetadataToken
在一个模块中是独一无二的 - 没有任何说法可以保证任何订单。
即使它确实按照您希望的方式运行,也会针对具体实施,并且可能随时更改,恕不另行通知。
查看旧的MSDN blog entry。
我强烈建议不要依赖此类实施细节 - 请参阅this answer from Marc Gravell。
如果你在编译时需要一些东西,你可以看看Roslyn(尽管它处于一个非常早期的阶段)。
答案 2 :(得分:11)
如果您要使用属性路线,这是我过去使用过的方法;
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链接到Marc Gravell的帖子中有一个很好的样本。
答案 3 :(得分:4)
我测试过MetadataToken排序的工作原理。
这里的一些用户声称这不是一个好方法/不可靠,但我还没有看到任何证据 - 也许你可以在给定方法不起作用时在这里发布一些代码snipet?
关于向后兼容性 - 当您正在使用.net 4 / .net 4.5时 - Microsoft正在制作.net 5或更高版本,因此您几乎可以假设此类排序方法将来不会被破坏。
当然,也许到2017年,当你升级到.net9时,你会遇到兼容性中断,但到那时微软人员可能会找出“官方排序机制”。回去或破坏东西是没有意义的。
使用属性排序的额外属性也需要时间和实现 - 为什么要在MetadataToken排序工作时烦恼?
答案 4 :(得分:1)
您可以在System.Component.DataAnnotations中使用DisplayAttribute,而不是自定义属性。无论如何,您的要求必须与显示有关。
答案 5 :(得分:0)
如果您对额外的依赖感到满意,可以使用Marc Gravell的Protobuf-Net来做到这一点,而不必担心实现反射和缓存等的最佳方法。只需使用[ProtoMember]
装饰您的字段然后使用以下命令按数字顺序访问字段:
MetaType metaData = ProtoBuf.Meta.RuntimeTypeModel.Default[typeof(YourTypeName)];
metaData.GetFields();
答案 6 :(得分:0)
我是这样做的:
internal static IEnumerable<Tuple<int,Type>> TypeHierarchy(this Type type)
{
var ct = type;
var cl = 0;
while (ct != null)
{
yield return new Tuple<int, Type>(cl,ct);
ct = ct.BaseType;
cl++;
}
}
internal class PropertyInfoComparer : EqualityComparer<PropertyInfo>
{
public override bool Equals(PropertyInfo x, PropertyInfo y)
{
var equals= x.Name.Equals(y.Name);
return equals;
}
public override int GetHashCode(PropertyInfo obj)
{
return obj.Name.GetHashCode();
}
}
internal static IEnumerable<PropertyInfo> GetRLPMembers(this Type type)
{
return type
.TypeHierarchy()
.SelectMany(t =>
t.Item2
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(prop => Attribute.IsDefined(prop, typeof(RLPAttribute)))
.Select(
pi=>new Tuple<int,PropertyInfo>(t.Item1,pi)
)
)
.OrderByDescending(t => t.Item1)
.ThenBy(t => t.Item2.GetCustomAttribute<RLPAttribute>().Order)
.Select(p=>p.Item2)
.Distinct(new PropertyInfoComparer());
}
声明属性如下:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class RLPAttribute : Attribute
{
private readonly int order_;
public RLPAttribute([CallerLineNumber]int order = 0)
{
order_ = order;
}
public int Order { get { return order_; } }
}
答案 7 :(得分:0)
在上述公认的解决方案的基础上,要获取准确的索引,您可以使用类似的方法
给予
public class MyClass
{
[Order] public string String1 { get; set; }
[Order] public string String2 { get; set; }
[Order] public string String3 { get; set; }
[Order] public string String4 { get; set; }
}
扩展名
public static class Extensions
{
public static int GetOrder<T,TProp>(this T Class, Expression<Func<T,TProp>> propertySelector)
{
var body = (MemberExpression)propertySelector.Body;
var propertyInfo = (PropertyInfo)body.Member;
return propertyInfo.Order<T>();
}
public static int Order<T>(this PropertyInfo propertyInfo)
{
return typeof(T).GetProperties()
.Where(property => Attribute.IsDefined(property, typeof(OrderAttribute)))
.OrderBy(property => property.GetCustomAttributes<OrderAttribute>().Single().Order)
.ToList()
.IndexOf(propertyInfo);
}
}
用法
var myClass = new MyClass();
var index = myClass.GetOrder(c => c.String2);
注意 ,没有错误检查或容错功能,您可以添加胡椒粉和盐调味
答案 8 :(得分:0)
另一种可能性是使用Client Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.0+k3s.1", GitCommit:"0f644650f5d8e9f091629f860b342f221c46f6d7", GitTreeState:"clean", BuildDate:"2020-01-06T23:20:30Z", GoVersion:"go1.13.5", Compiler:"gc", Platform:"linux/arm"}
Server Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.0+k3s.1", GitCommit:"0f644650f5d8e9f091629f860b342f221c46f6d7", GitTreeState:"clean", BuildDate:"2020-01-06T23:20:30Z", GoVersion:"go1.13.5", Compiler:"gc", Platform:"linux/arm"}```
System.ComponentModel.DataAnnotations.DisplayAttribute
属性。
由于它是内置的,因此无需创建新的特定属性。
然后选择像这样的有序属性
Order
班级可以这样呈现
const int defaultOrder = 10000;
var properties = type.GetProperties().OrderBy(p => p.FirstAttribute<DisplayAttribute>()?.GetOrder() ?? defaultOrder).ToArray();