C# 保证列表中属性的顺序与它们在代码文件中的显示顺序相匹配

C# 保证列表中属性的顺序与它们在代码文件中的显示顺序相匹配,c#,list,reflection,c#-2.0,C#,List,Reflection,C# 2.0,我有一个接口,它定义了一个返回IList的方法: 公共接口可写 { IList可写属性(); } . . 它以以下方式在各种(不同)类中实现: public abstract class Foo { private IList<PropertyInfo> _props; protected Foo() { this._props = new List<PropertyInfo>(); foreach (Prop

我有一个接口,它定义了一个返回
IList
的方法:

公共接口可写
{
IList可写属性();
}
.
.
它以以下方式在各种(不同)类中实现:

public abstract class Foo
{
    private IList<PropertyInfo> _props;

    protected Foo()
    {
        this._props = new List<PropertyInfo>();

        foreach (PropertyInfo p in this.GetType().GetProperties())
        {
            if (Attribute.IsDefined(p, typeof(WriteableAttribute)))
                this._props.Add(p);
        }
    }

    #region IWriteable Members

    public IList<PropertyInfo> WriteableProperties()
    {
        return this._props;
    }

    #endregion
}

public class Bar : Foo
{
    public string A
    {
        get { return "A"; }
    }

    [Writeable()]
    public string B
    {
        get { return "B"; }
    }

    [Writeable()]
    public string C
    {
        get { return "C"; }
    }

    // Snip
}
公共抽象类Foo
{
私人IList_道具;
受保护的Foo()
{
这个._props=new List();
foreach(PropertyInfo p在此.GetType().GetProperties()中)
{
if(Attribute.IsDefined(p,typeof(WriteableAttribute)))
本._-props.Add(p);
}
}
#区域可写成员
公共IList可写属性()
{
归还这个;
}
#端区
}
公共类酒吧:富
{
公共字符串A
{
获取{返回“A”;}
}
[可写()]
公共字符串B
{
获取{返回“B”;}
}
[可写()]
公共字符串C
{
获取{返回“C”;}
}
//剪断
}
请注意标记两个属性的属性,因为这些属性将被添加到列表中。然后,在某些文件写入操作期间,将在其他地方使用此
IList

它们在列表中的顺序与它们在代码文件中出现的顺序对我来说很重要。

然而,缔约国:

GetProperties方法不返回特定类型中的属性 顺序,如字母顺序或声明顺序。您的代码不能 取决于返回属性的顺序,因为 顺序各不相同

那么,确保每个PropertyInfo按我希望的顺序添加的最佳方法是什么


(我也在使用.NET2.0,因此我不能使用任何Linq goods,如果有任何Linq goods会有所帮助,尽管这很有趣。)

将有关排序的信息添加到属性中,然后您可以使用它来确保排序,例如:

[Writeable(Order = 1)]
因此,对于以下属性:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class WriteableAttribute : Attribute
{
    public int Order { get; set; }
}
您可以按如下顺序选择属性:

private readonly List<PropertyInfo> _props;

protected Foo()
{
    _props = new List<PropertyInfo>();

    var props = new Dictionary<int, PropertyInfo>();

    foreach (PropertyInfo p in GetType().GetProperties())
    {
        if (Attribute.IsDefined(p, typeof(WriteableAttribute)))
        {
            var attr = (WriteableAttribute)p
                .GetCustomAttributes(typeof(WriteableAttribute))[0];

            props.Add(attr.Order, p);
        }
    }

    _props.AddRange(props.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value));
}
private readonly List\u props;
受保护的Foo()
{
_props=新列表();
var props=newdictionary();
foreach(GetType()中的PropertyInfo p.GetProperties())
{
if(Attribute.IsDefined(p,typeof(WriteableAttribute)))
{
var attr=(WriteableAttribute)p
.GetCustomAttributes(typeof(WriteableAttribute))[0];
道具添加(属性顺序,p);
}
}
_AddRange(props.OrderBy(kvp=>kvp.Key).Select(kvp=>kvp.Value));
}
NB对于生产代码,我建议缓存属性信息(例如,每种类型),因为如果对每个实例进行缓存,速度会相对较慢

更新-缓存

通过一些属性查找和排序的缓存示例:

public static class PropertyReflector
{
    private static readonly object SyncObj = new object();

    private static readonly Dictionary<Type, List<PropertyInfo>> PropLookup =
        new Dictionary<Type, List<PropertyInfo>>();

    public static IList<PropertyInfo> GetWritableProperties(Type type)
    {
        lock (SyncObj)
        {
            List<PropertyInfo> props;

            if (!PropLookup.TryGetValue(type, out props))
            {
                var propsOrder = new Dictionary<int, PropertyInfo>();

                foreach (PropertyInfo p in type.GetProperties())
                {
                    if (Attribute.IsDefined(p, typeof(WriteableAttribute)))
                    {
                        var attr = (WriteableAttribute)p.GetCustomAttributes(
                            typeof(WriteableAttribute), inherit: true)[0];

                        propsOrder.Add(attr.Order, p);
                    }
                }

                props = new List<PropertyInfo>(propsOrder
                    .OrderBy(kvp => kvp.Key)
                    .Select(kvp => kvp.Value));

                PropLookup.Add(type, props);
            }

            return props;
        }
    }
}
公共静态类PropertyReflector
{
私有静态只读对象SyncObj=新对象();
专用静态只读字典查找=
新字典();
公共静态IList GetWritableProperties(类型)
{
锁(同步对象)
{
列出道具;
if(!PropLookup.TryGetValue(类型,out props))
{
var propsOrder=新字典();
foreach(type.GetProperties()中的PropertyInfo p)
{
if(Attribute.IsDefined(p,typeof(WriteableAttribute)))
{
var attr=(WriteableAttribute)p.GetCustomAttributes(
typeof(WriteableAttribute),inherit:true[0];
添加(属性顺序,p);
}
}
props=新列表(propsOrder)
.OrderBy(kvp=>kvp.Key)
.选择(kvp=>kvp.Value));
添加(类型,道具);
}
返回道具;
}
}
}
更新-无Linq

可以使用以下代码替换Linq部分,以对属性进行排序并将其添加到缓存中:

List<int> order = new List<int>(propsOrder.Keys);
order.Sort();

props = new List<PropertyInfo>();

order.ForEach(i => props.Add(propsOrder[i]));

PropLookup.Add(type, props);
列表顺序=新列表(propsOrder.Keys);
order.Sort();
props=新列表();
order.ForEach(i=>props.Add(propsOrder[i]);
添加(类型,道具);
更新-完整Linq

并使用完全Linq解决方案:

static IList<PropertyInfo> GetWritableProperties(Type type)
{
    lock (SyncObj)
    {
        List<PropertyInfo> props;

        if (!PropLookup.TryGetValue(type, out props))
        {
            props = type.GetProperties()
                .Select(p => new { p, Atts = p.GetCustomAttributes(typeof(WriteableAttribute), inherit: true) })
                .Where(p => p.Atts.Length != 0)
                .OrderBy(p => ((WriteableAttribute)p.Atts[0]).Order)
                .Select(p => p.p)
                .ToList();

            PropLookup.Add(type, props);
        }

        return props;
    }
}
静态IList GetWritableProperties(类型)
{
锁(同步对象)
{
列出道具;
if(!PropLookup.TryGetValue(类型,out props))
{
props=type.GetProperties()
.Select(p=>new{p,Atts=p.GetCustomAttributes(typeof(WriteableAttribute),inherit:true)})
.其中(p=>p.Atts.Length!=0)
.OrderBy(p=>((WriteableAttribute)p.Atts[0]).Order)
.选择(p=>p.p)
.ToList();
添加(类型,道具);
}
返回道具;
}
}

不久前,当我遇到同样的问题时,我编写了一个helper类,根据属性的
顺序
属性对属性进行排序。我使用了内置的
DisplayAttribute
,但您只需将
Order
属性添加到您编写的任何属性中即可

class FieldSorter : IComparer, IComparer<DisplayAttribute>, IEqualityComparer<DisplayAttribute>
{
    public int Compare(object x, object y)
    {
        return Compare((DisplayAttribute)x, (DisplayAttribute)y);
    }
    public int Compare(DisplayAttribute x, DisplayAttribute y)
    {
        return x.Order.CompareTo(y.Order);
    }
    public bool Equals(DisplayAttribute x, DisplayAttribute y)
    {
        return Compare(x, y) == 0;
    }
    public int GetHashCode(DisplayAttribute obj)
    {
        return obj.GetHashCode();
    }

    public static SortedList<DisplayAttribute, PropertyInfo> GetSortedFields(Type type)
    {
        PropertyInfo[] props = type.GetProperties();
        var sortedProps = new SortedList<DisplayAttribute, PropertyInfo>(props.Length, new FieldSorter());
        object[] atts;
        int assignedOrder = 1000; // anything without pre-assigned order gets a ridiculously high order value. same for duplicates.
        foreach (var prop in props)
        {
            atts = prop.GetCustomAttributes(typeof(DisplayAttribute), true);
            if (atts.Length > 0)
            {
                var att = (DisplayAttribute)atts[0];
                if (!att.GetOrder().HasValue || sortedProps.Keys.Contains(att, new FieldSorter()))
                    att.Order = assignedOrder++;
                sortedProps.Add(att, prop);
            }
        }
        return sortedProps;
    }
}
输出:


在订单很重要的地方你在做什么?创建的文件需要为第三方使用某种格式。@chibacity:回答得好,谢谢。虽然我使用的是.NET2.0,但我不能使用.Select扩展方法,因此必须对其进行修改。另外,缓存属性信息是什么意思?它已经缓存在私有列表中了,不是吗?或者你是说别的什么?@Andy在我的示例中,属性信息将缓存在每个类类型中,而不是缓存在你创建的每个实例中。效率更高。@Andy我已将示例代码重构为一个实用程序类,这样您就不会被迫进入继承层次结构以获得所需的行为。我还包括Linq和N
class FieldSorter : IComparer, IComparer<DisplayAttribute>, IEqualityComparer<DisplayAttribute>
{
    public int Compare(object x, object y)
    {
        return Compare((DisplayAttribute)x, (DisplayAttribute)y);
    }
    public int Compare(DisplayAttribute x, DisplayAttribute y)
    {
        return x.Order.CompareTo(y.Order);
    }
    public bool Equals(DisplayAttribute x, DisplayAttribute y)
    {
        return Compare(x, y) == 0;
    }
    public int GetHashCode(DisplayAttribute obj)
    {
        return obj.GetHashCode();
    }

    public static SortedList<DisplayAttribute, PropertyInfo> GetSortedFields(Type type)
    {
        PropertyInfo[] props = type.GetProperties();
        var sortedProps = new SortedList<DisplayAttribute, PropertyInfo>(props.Length, new FieldSorter());
        object[] atts;
        int assignedOrder = 1000; // anything without pre-assigned order gets a ridiculously high order value. same for duplicates.
        foreach (var prop in props)
        {
            atts = prop.GetCustomAttributes(typeof(DisplayAttribute), true);
            if (atts.Length > 0)
            {
                var att = (DisplayAttribute)atts[0];
                if (!att.GetOrder().HasValue || sortedProps.Keys.Contains(att, new FieldSorter()))
                    att.Order = assignedOrder++;
                sortedProps.Add(att, prop);
            }
        }
        return sortedProps;
    }
}
        public class Stats 
        {
            [Display(Name = "Changes", Description = "Changed records.", Order = 8)]
            public int RecordsWithChanges { get; set; }
            [Display(Name = "Invalid", Description = "Number of invalid records analyzed.", Order = 4)]
            public int InvalidRecordCount { get; set; }
            [Display(Name = "Valid", Description = "Number of valid records.", Order = 6)]
            public int ValidRecordCount { get; set; }
            [Display(Name = "Cost", Description = "Number of records with a Cost value.", Order = 10)]
            public int RecordsWithCost { get; set; }
            public Stats(int changed, int valid, int invalid, int cost)
            {
                RecordsWithChanges = changed;
                ValidRecordCount = valid;
                InvalidRecordCount = invalid;
                RecordsWithCost = cost;
            }
        }

        class Program
        {
            static void Main(string[] args)
            {
                var foo = new Stats(123, 456, 7, 89);
                var fields = FieldSorter.GetSortedFields(foo.GetType());
                foreach (DisplayAttribute att in fields.Keys)
                    Console.WriteLine("{0}: {1} ({2}) == {3}", 
                        att.Order, att.Name, att.Description, fields[att].GetValue(foo, null));
null));

            }
        }
4: Invalid (Number of invalid records analyzed.) -- 7
6: Valid (Number of valid records.) -- 456
8: Changes (Changed records.) -- 123
10: Cost (Number of records with a Cost value.) -- 89