【C#】属性をつけてプロパティをソートする

背景

公式ドキュメントに「GetProperties は、アルファベット順や宣言順など、特定の順序でプロパティを返すのではありません」と記載がありました。

自分が試した限りでは、定義した順序通り出力されていたましたが、公式で保証しないと言われている以上、対策しないわけにはいきません!

ということで、順序を保証するためにエンティティのプロパティに属性を付けて対応しました。

準備

カスタム属性の作成

プロパティは読み取り専用として実装します。

public class Attributes
{
    public class OrderAttribute : System.Attribute
    {
        public OrderAttribute(int value)
        {
            this.value = value;
        }
        public int value { get; private set; }
    }
}

エンティティクラスの実装

先ほど作成したカスタム属性を設定したエンティティクラスを作成します。

※型名のサフィックス(Attribute)は省略することが可能です。

public class Item
{
    [Order(2)]
    public int Id { get; set; }

    [Order(1)]
    public string Name { get; set; }

    [OrderAttribute(0)]
    public int Price { get; set; }

    public int SupplierId { get; set; }

    public string SupplierName { get; set; }
}

LINQを使用したソート

属性が設定されているもののみ抽出し、プロパティ名を出力します。

private static void SortTest01<T>()
{
    var pNames = typeof(T).GetProperties()
                        .Where(p => Attribute.IsDefined(p, typeof(OrderAttribute)))
                        .OrderBy(p => ((OrderAttribute)Attribute
                                        .GetCustomAttribute(p, typeof(OrderAttribute)
                                        )).value
                                )
                        .Select(p => p.Name);

    foreach(string pName in pNames)
    {
        Console.WriteLine(pName);
    }
}

出力結果

Price
Name
Id

属性の有無で取得を分ける

属性が設定されているものを優先的に取得し、その後属性が設定されていないものを取得します。これにより属性が混在していてもすべてのデータを取得することが可能になります。

private static void SortTest02<T>()
{
    var props = TypeDescriptor.GetProperties(typeof(T));

    // 属性の並び順設定用辞書
    var propDic = new Dictionary<int, string>();
    var noProps = new List<string>();
    typeof(T).GetProperties().ToList().ForEach(
        p =>
        {
            if (System.Attribute.IsDefined(p, typeof(OrderAttribute)))
            {
                OrderAttribute orderAtt = (OrderAttribute)Attribute
                                        .GetCustomAttribute(p, typeof(OrderAttribute)
                                        );
                if (orderAtt != null)
                {
                    propDic.Add(orderAtt.value, p.Name);
                }
            }
            else
            {
                OrderAttribute orderAtt = (OrderAttribute)Attribute
                                        .GetCustomAttribute(p, typeof(OrderAttribute)
                                        );
                if (orderAtt == null)
                {
                    noProps.Add(p.Name);
                }
            }
        });

    // 属性あり
    foreach(string pName in propDic.OrderBy(p => p.Key).Select(p => p.Value))
    {
        Console.WriteLine(pName);
    }

    // 属性なし
    foreach(string pName in noProps)
    {
        Console.WriteLine(pName);
    }
}

出力結果

Price
Name
Id
SupplierId
SupplierName

参考資料

Type.GetProperties メソッド (System)
現在の Type のプロパティを取得します。
タイトルとURLをコピーしました