列表中属性的保证顺序与它们在代码文件中出现的顺序相匹配

And*_*ndy 2 c# reflection list c#-2.0

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

public interface IWriteable
{
    IList<PropertyInfo> WriteableProperties();
}
Run Code Online (Sandbox Code Playgroud)

.
.
它以下列方式在各种(不同的)类中实现:

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
}
Run Code Online (Sandbox Code Playgroud)

请注意标记几个属性的属性,因为这些属性将添加到列表中.这IList将在某些文件写入操作期间在其他地方使用.

对我来说很重要的是,它们按照它们出现在代码文件中的顺序在列表中排序.

但是,MSDN声明:

GetProperties方法不以特定顺序返回属性,例如按字母顺序或声明顺序.您的代码不得依赖于返回属性的顺序,因为该顺序会有所不同.

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

(我也使用.NET2.0,所以我不能使用任何Linq的优点,如果有任何帮助,虽然看起来会很有趣.)

Tim*_*oyd 6

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

[Writeable(Order = 1)]
Run Code Online (Sandbox Code Playgroud)

因此,对于以下属性:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class WriteableAttribute : Attribute
{
    public int Order { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

您可以按如下方式获得有序选择的属性:

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));
}
Run Code Online (Sandbox Code Playgroud)

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

更新 - 没有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);
Run Code Online (Sandbox Code Playgroud)

更新 - Full 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;
    }
}
Run Code Online (Sandbox Code Playgroud)