プログラムを中心とした個人的なメモ用のブログです。 タイトルは迷走中。
内容の保証はできませんのであしからずご了承ください。

2020/06/25

[C#] nameof 演算子の代わりにフルネームを返すメソッドを作る

update2020/06/25 event_note2020/06/24 23:21

nameof 演算子は最後の名前しか返しませんが、フルネームを取得したいことは多々あります。

そんななか、stack overflow に便利そうなコードがあったので、そのまま転載します。

例えば、以下のようなクラスがあったとして、

class SpeciesFamily
{
    public string Name { get; set; }
}

class Species
{
    public SpeciesFamily Family { get; set; }
    public string Name { get; set; }
}

class Cat
{
    public Species Species { get; set; }
}

Species.Family.Name という文字列を nameof 演算子を使って取得しようとすると、以下のようなコードになってしまいます。

var fullName = $"{nameof(Species)}.{nameof(Species.Family)}.{nameof(SpeciesFamily.Name)}"

同じクラス名やプロパティ名を何度も書くことになるので、階層が深くなればなるほど面倒です。

これに対して、フルネームを簡単に取得できるようにしたメソッドのサンプルが以下の2つです。
(階層が浅いと記述量はあまり変わりませんが・・・)

サンプルコード1

public static string GetMemberString<T>(System.Linq.Expressions.Expression<Func<T, object>> member)
{
    if (member == null)
    {
        throw new ArgumentNullException("member");
    }

    var propertyRefExpr = member.Body;
    var memberExpr = propertyRefExpr as System.Linq.Expressions.MemberExpression;

    if (memberExpr == null)
    {
        var unaryExpr = propertyRefExpr as System.Linq.Expressions.UnaryExpression;

        if (unaryExpr != null && unaryExpr.NodeType == System.Linq.Expressions.ExpressionType.Convert)
        {
            memberExpr = unaryExpr.Operand as System.Linq.Expressions.MemberExpression;

            if (memberExpr != null)
            {
                return memberExpr.Member.Name;
            }
        }
    }
    else
    {
        //gets something line "m.Field1.Field2.Field3", from here we just remove the prefix "m."
        string body = member.Body.ToString();
        return body.Substring(body.IndexOf('.') + 1);
    }

    throw new ArgumentException("No property reference expression was found.", "member");
}

使い方

// Will return a string containing "Species.Family.Name".
var fullName = GetMemberString<Cat>(x => x.Species.Family.Name)

サンプルコード2

public static class NameOf<TSource>
{
    #region Public Methods

    public static string Full(Expression<Func<TSource, object>> expression)
    {
        var memberExpression = expression.Body as MemberExpression;
        if (memberExpression == null)
        {
            if (expression.Body is UnaryExpression unaryExpression && unaryExpression.NodeType == ExpressionType.Convert)
                memberExpression = unaryExpression.Operand as MemberExpression;
        }

        var result = memberExpression.ToString();
        result = result.Substring(result.IndexOf('.') + 1);

        return result;
    }

    public static string Full(string sourceFieldName, Expression<Func<TSource, object>> expression)
    {
        var result = Full(expression);
        result = string.IsNullOrEmpty(sourceFieldName) ? result : sourceFieldName + "." + result;
        return result;
    }

    #endregion
}

使い方

// Will return a string containing "Species.Family.Name".
var fullName = NameOf<Cat>.Full(c => c.Species.Family.Name);

// Will return a string containing "cat.Species.Name".
var fullNameWithPrefix = NameOf<Cat>.Full("cat", c => c.Species.Name);

"cat" のようにプレフィックスが指定できるので、こちらのほうがより汎用的かと思います。

また、"cat" の部分も nameof 演算子を使えば、文字列の記述を完全に無くすことができます。