我正在使用https://stackoverflow.com/a/531388/528131中的代码从基础成功检索对象实例的所有属性,问题是派生类型的属性首先被迭代。由于协议的性质,我首先需要基本属性。
x |
y | B
z |
w | A
B和A是类,B来自A. x,y,z是来自B的属性,w是来自A的属性
这是A.GetProperties();正在回归我需要这个:
w | A
x |
y | B
z |
有没有办法按顺序获取字段?
答案 0 :(得分:16)
类型中的字段不是“有序”的。这些方法中项目的排序是一个实现细节,强烈建议不要依赖它们。
你应该自己订购这些物品,期望它们可以从任何位置开始,以确保你的程序健壮而不是脆弱。
由于可以要求每个属性声明它的类型,您可以在开头创建一个查找,为层次结构中的每个类提供一个数字,从您开始的类型一直到object
by遍历BaseType
的{{1}}属性,并按每个属性的声明类型的查找值排序:
Type
答案 1 :(得分:2)
反射子系统的文档说明您不能依赖于返回元素的顺序。
也就是说,我一直认为元素是按源文件中的声明顺序返回的。在Mono或.NET的未来版本中,这可能是也可能不是。
您最好的选择是,如果您希望继续进行上述操作,请使用BindingFlags.DeclaredOnly
选项并手动遍历继承层次结构(在子类型之前扫描基类型以按正确顺序获取它们)。您应该编写代码,使得单个声明类型的属性排序无关紧要(例如,按名称对它们进行排序);如果.NET框架的行为发生变化,这将使您的代码更加健壮。
Fasterflect这样做(主要是为了能够过滤掉已被覆盖的虚拟属性)。它还有助手使用它自己的更强大的Flags
选择器参数来获取或不过滤的属性。
如果单个类型中的元素排序不重要,您可以像这样获取列表(使用Fasterflect):
var properties = type.Properties().Reverse().ToList();
您应该知道,当以这种方式反映时(通过遍历并仅获取声明的属性),将多次包含被覆盖的属性。 Fasterflect提供了从结果中过滤这些选项的选项:
var properties = type.Properties( Flags.InstancePublic | Flags.ExcludeBackingMembers ).Reverse().ToList();
如果您不希望依赖库,则代码是开源的,因此您可以选择所需的位。遍历算法可以是seen here(第443行)。
答案 2 :(得分:1)
所有您需要做的就是声明类型并反转列表
var publicProperties = typeof(T).GetProperties()
.GroupBy(p => p.DeclaringType)
.Reverse()
.SelectMany(g => g)
.ToArray();
答案 3 :(得分:0)
作为另一种分类方法:
PropertyInfo[] properties = type.GetProperties(...);
Array.Sort(properties, (pi1, pi2) =>
{
if (pi1.DeclaringType.IsSubclassOf(pi2.DeclaringType))
return 1;
else if (pi2.DeclaringType.IsSubclassOf(pi1.DeclaringType))
return -1;
else
return 0;
});
答案 4 :(得分:-1)
您可以从PropertyInfo
的实例获取声明类型,并按Object
的距离进行排序。
我会这样做:
void Main()
{
typeof(B).GetProperties()
.Select((x,i) => new {
Prop = x,
DeclareOrder = i,
InheritanceOrder = GetDepth(x.DeclaringType),
})
.OrderByDescending(x => x.InheritanceOrder)
.ThenBy(x => x.DeclareOrder)
.Dump();
}
public class A
{
public string W {get; set;}
}
public class B : A
{
public string X {get; set;}
public string Y {get; set;}
public string Z {get; set;}
}
static int GetDepth(Type t)
{
int depth = 0;
while (t != null)
{
depth++;
t = t.BaseType;
}
return depth;
}