enumの日本語別名とか三項演算子ネストとか

enumのToStringで日本語名になって欲しいというケースはとてもあるある。enum に文字列の属性を: いげ太のブログ2008-01-24 - 当面C#と.NETな記録の記事を見て、今まで拡張メソッドで処理することが多かったのですが、やっぱり見た目は属性のほうがスッキリするなあ、と思った。記述が本体と離れないのが良いですよね。処理速度とか、そんなに頻繁に繰り返し繰り返し呼ぶものでもないしリフレクション上等!それ気にしたらSerializeとか出来ない!とか思ったので、基本は属性で処理することにしてみた。

enum Fruit
{
    [AliasName("ブドウ")]
    Grape,
    [AliasName("リンゴ")]
    Apple,
    [AliasName("オレンジ")]
    Orange
}

static void Main(string[] args)
{
    var fruit = Fruit.Orange;
    Console.WriteLine(fruit.ToAliasName());
}

こんな感じに定義してこんな感じに使う、と。定義もスッキリ、呼び出し時もToString的に拡張メソッドでスッキリ。

[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class AliasNameAttribute : Attribute
{
    public string AliasName { get; private set; }

    public AliasNameAttribute(string aliasName)
    {
        AliasName = aliasName;
    }
}

public static class Ext
{
    // どうしてもメソッドチェインを崩したくない人用
    public static T ThrowIf<T>(this T value, Func<T, bool> predicate, Exception exception)
    {
        if (predicate(value)) throw exception;
        else return value;
    }

    public static string ToAliasName(this Enum value)
    {
        return value.GetType()
            .GetField(value.ToString())
            .GetCustomAttributes(typeof(AliasNameAttribute), false)
            .Cast<AliasNameAttribute>()
            .FirstOrDefault()
            .ThrowIf(a => a == null, new ArgumentException("属性が設定されていません。"))
            .AliasName;
    }
}

ドットが縦にならんでると何だか楽しい。というわけで、チェーンを崩さずに例外を放り投げるための拡張メソッドThrowIfを用意してみた。nullが邪魔!邪魔!nullが出現するせいで一個変数を置いてnullチェックかまさなきゃいけない!というシーンは多いので、predicateじゃなくてnull限定決め打ちでも良いぐらいかもかも。とにかくnull撲滅。まあ、この場合はFirstOrDefaultじゃなくてFirstにすれば、Firstが例外を吐いてくれるのですけど、一応ちゃんとメッセージ用意したいとか、ありますよね?ね?

// 三項演算子のチェーンを崩さないためにダミーの型を返して例外を投げる
static T Throw<T>(Exception exception)
{
    throw exception;
}

static string Test(Fruit fruit)
{
    return (fruit == Fruit.Apple) ? "あぷる"
         : (fruit == Fruit.Grape) ? "ぐれえぷ"
         : (fruit == Fruit.Orange) ? "おれんじ"
         : Throw<string>(new ArgumentException("引数ダメぽ!"));
}

三項演算子を延々とネストしてコロンを前置にするのが好き、と以前に書いたのですが、そうすると、最後にdefault的なものを書く必要があって困る。""とか0とかでお茶を濁さずに、例外を吐きたいのですが、ネスト三項演算子では例外を吐けない。困った困った。というわけでThrowという補助メソッドを用意してみた。ただ例外を吐くだけメソッド。おー。これでもう大量にネストしても大丈夫!万歳!三項演算子でネストしよう!ついでにネスト時のコロンは前置にしよう!の会。

使用しないでください?

ところで、EnumのToStringはIntelliSenseに使用しないでください、が出てきてビビる。んが、よくよく眺めてみると……

public override string ToString();
[Obsolete("The provider argument is not used. Please use ToString().")]
public string ToString(IFormatProvider provider);
public string ToString(string format);
[Obsolete("The provider argument is not used. Please use ToString(String).")]
public string ToString(string format, IFormatProvider provider);

ObsoleteなのはIFormatProviderが絡んでるものだけで、普通のToStringは使用していいんでないのん?文字列の列挙体←ここのコメント欄が白熱していたのを見て、どうなのかなー、と思っているのですが、どうなんでしょうか。いやほんと。

Profile

Yoshifumi Kawai

Cysharp, Inc
CEO/CTO

Microsoft MVP for Developer Technologies(C#)
April 2011
|
July 2024

Twitter:@neuecc GitHub:neuecc

Archive