Roslyn で Expression に含まれる Enum の値が取れなくなったと思ったらバグだった

例えば、以下のコードを VS2013 で動かすと最終的に NumberEnum.One という定数から "1" という値が取れます。

class Hoge
{
    public NumberEnum NumberEnum { get; set; }
}

enum NumberEnum
{
    Zero,
    One
}

static void Main(string[] args)
{
    Expression<Func<Hoge, bool>> expression = _ => _.NumberEnum == NumberEnum.One;

    var bin = (BinaryExpression) expression.Body;
    var r = bin.Right;
    var c = r as ConstantExpression;
    var value = c.Value;

    Console.WriteLine("value: " + value); // "value: 1"
}

ところが、これと同じコードを VS2015 で動かすと、var c = r as ConstantExpression; でキャストに失敗して次の行でヌルポになります。

r の型が変わってしまっているわけですが、ちょっとデバッガで見てみましょう。

f:id:kendik:20151006223011p:plain

NodeType プロパティの値が "Convert" になっていますね。VS2013 ではここは "Constant" でした。
Roslyn で仕様変更があったのかと思って調べてみたんですが、そうではなくどうやら普通にバグだったようです。既にプルリクエストも出ており、マージ済みでした。

issue
github.com github.com

pull request
github.com

例であげたコードで言うと、
Expression<Func<Hoge, bool>> expression = _ => _.NumberEnum == NumberEnum.One;
から取れる Body の値が変わってしまっているわけですね。比較するとこうなります。

VS2013: (Convert(_.NumberEnum) == 1)
VS2015: (Convert(_.NumberEnum) == Convert(One))

Roslyn は現在 v1.1 の開発が進んでいるようですが、そちらに含まれることになるんでしょう。

対策

まぁそれはそれで待つとして、とりあえず今どうするかなんですが、色々こねくり回した結果どうしても "1" は取れず "One" という値しか取れないのでこうなりました。

Expression<Func<Hoge, bool>> expression = _ => _.NumberEnum == NumberEnum.One;

var bin = (BinaryExpression)expression.Body;
var r = bin.Right;

// var c = r as ConstantExpression;
// var value = c.Value;

var u = r as UnaryExpression;
var c = u.Operand as ConstantExpression;
var value = (int) ((NumberEnum) c.Value);

Console.WriteLine("value: " + value); // "value: 1"

うわぁこれはひどい。。。どうにかならないですかね。