1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C# - プロパティに付与した属性の値を取得したいときにプロパティ名を文字列で指定したくない

Last updated at Posted at 2024-10-26

初めに

この記事で使用している.NETのバージョンは8です。

C#初心者が調べたことをまとめているため、不備・不足あるかもしれませんがご了承ください。

改善したいコード

プロパティに付与した属性の値を取得したいときは、以下のようにリフレクションを用いて「プロパティ名」を文字列で指定して得るというのが一つ手段としてあるかなと思います。

public class LogicalNameAttribute(string value) : Attribute
{
    public string Value { get; } = value;
}
public class Person
{
    [LogicalName("名前")]
    public string? Name { get; set; }

    [LogicalName("年齢")]
    public int? Age { get; set; }
}
using System.Reflection;
public class Program
{
    public static void Main(string[] args)
    {
        var nameP = typeof(Person).GetProperty("Name");
        var nameLN = nameP?.GetCustomAttribute<LogicalNameAttribute>()?.Value;
        var ageP = typeof(Person).GetProperty("Age");
        var ageLN = ageP?.GetCustomAttribute<LogicalNameAttribute>()?.Value;

        Console.WriteLine($"Name : {nameLN}, Age : {ageLN}");
    }
}

↓出力結果

Name : 名前, Age : 年齢

ただ、せっかくPersonクラスでプロパティとして定義しているので、文字列を指定するのではなくプロパティ自体をEnumのような感じで指定できればいいなと考えました。
また、プロパティ名を変更するとしたとき、Visual Studioとかだとよしなに変更を追従してくれますが、文字列で指定していたら修正漏れが発生するかもしれません。

改善コード案1

上記のことから、調べたりした結果の改善案が以下です(記載のないクラスは上記と同様)。

using System.Linq.Expressions;
using System.Reflection;
public class Program
{
    public static void Main(string[] args)
    {
        var nameLN = GetValue(x => x.Name);
        var ageLN = GetValue(x => x.Age);
        Console.WriteLine($"Name : {nameLN}, Age : {ageLN}");
    }

    public static string? GetValue<T>(Expression<Func<Person, T>> expression)
    {
        var memberExpression = expression?.Body as MemberExpression;
        var property = memberExpression?.Member as PropertyInfo;
        return property?.GetCustomAttribute<LogicalNameAttribute>()?.Value;
    }
}

式ツリーを引数の型としたGetValueメソッドを新たに作成しました。例のようにプロパティを呼び出すラムダ式を渡して使用します。
「プロパティ名の文字列」を指定せずに、Personクラスの構成要素だけを用いて属性の値を得ることができました。
補完が効きますから、誤った文字列を指定してしまった結果のエラーとかは回避できるのではないでしょうか。

もちろん、ラムダ式で書く必要があるという意味では、無駄が発生しているデメリットはあるかなと思います。

改善コード案2

「改善コード案1」では、GetValueクラスにPersonクラスを指定していましたが、もっと汎用的に使いたい場面があると思います。
それに対応した改善案について調べた結果が以下のコードです。

public class Program
{
    public static void Main(string[] args)
    {
        var nameLN = GetValue<Person>(x => x.Name);
        var ageLN = GetValue<Person>(x => x.Age);
        Console.WriteLine($"Name : {nameLN}, Age : {ageLN}");
    }

    public static string? GetValue<T>(Expression<Func<T, object?>> expression)
    {
        var memberExpression = expression?.Body as MemberExpression;
        if (memberExpression == null && expression?.Body is UnaryExpression unaryExpression)
        {
            memberExpression = unaryExpression?.Operand as MemberExpression;
        }
        var property = memberExpression?.Member as PropertyInfo;
        return property?.GetCustomAttribute<LogicalNameAttribute>()?.Value;
    }
}

GetValueメソッドを改修しました。

処理中にUnaryExpressionが登場していますが、プロパティの型が値型(intとか?)だと、ボックス化の関係でUnaryExpressionに変換されるらしいです。そのため、UnaryExpressionからMemberExpressionを取り出す処理が増えています。

改善コード案3

コメントで頂いた案です。
nameofの中では「クラス名.プロパティ名」で指定できるようです。
以下が改善コード案です。

using System.Reflection;
public class Program
{
    public static void Main(string[] args)
    {
        var nameP = typeof(Person).GetProperty(nameof(Person.Name));
        var nameLN = nameP?.GetCustomAttribute<LogicalNameAttribute>()?.Value;
        var ageP = typeof(Person).GetProperty(nameof(Person.Age));
        var ageLN = ageP?.GetCustomAttribute<LogicalNameAttribute>()?.Value;

        Console.WriteLine($"Name : {nameLN}, Age : {ageLN}");
    }
}

「改善したいコード」を改善するならばこれで問題ありませんね。

参考

1
2
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?