式木は評価が高コストで、構築はそれほどでも無いと思っていたら、どうやら勘違いだったみたい。
サンプルを作って実験してみた。
using System; using System.Diagnostics; using System.Linq.Expressions; namespace RequiresSample { public static class ExpressionUtil { // 式木から変数名を取得する public static string GetMemberName<T>(Expression<Func<T>> lambda) { var memberEx = (MemberExpression)lambda.Body; return memberEx.Member.Name; } } public static class Requires { // 変数名を式木から取得する public static void NotNull<T>(T value, Expression<Func<T>> lambda) { NotNull(value, ExpressionUtil.GetMemberName(lambda)); } // 変数名を文字列で取得する public static void NotNull<T>(T value, string name) { if (value == null) { throw new ArgumentNullException(name); } } } public static class RequiresEx { // 変数名を式木から取得する // 式木の評価は例外を発生させるときまで遅延 public static void NotNull<T>(T value, Expression<Func<T>> lambda) { if (value == null) { throw new ArgumentNullException(ExpressionUtil.GetMemberName(lambda)); } } } class Program { private const int COUNT = 100000; static void Main(string[] args) { string key="test"; // 変数名を文字列で渡したとき var sw = Stopwatch.StartNew(); for (int i = 0; i < COUNT; i++) { Requires.NotNull(key, "key"); } sw.Stop(); Console.WriteLine( "Requires.NotNull<T>(T value, string name) : {0} ms", sw.ElapsedMilliseconds); // 変数名を式木から取り出すとき sw.Reset(); sw.Start(); for (int i = 0; i < COUNT; i++) { Requires.NotNull(key, () => key); } sw.Stop(); Console.WriteLine( "Requires.NotNull<T>(T value, Expression<Func<T>> lambda) : {0} ms", sw.ElapsedMilliseconds); // 式木の評価を遅延させたとき sw.Reset(); sw.Start(); for (int i = 0; i < COUNT; i++) { RequiresEx.NotNull(key, () => key); } sw.Stop(); Console.WriteLine( "RequiresEx.NotNull<T>(T value, Expression<Func<T>> lambda) : {0} ms", sw.ElapsedMilliseconds); Console.ReadLine(); } } }
この結果がこちら。
Requires.NotNull<T>(T value, string name) : 2 ms Requires.NotNull<T>(T value, Expression<Func<T>> lambda) : 664 ms RequiresEx.NotNull<T>(T value, Expression<Func<T>> lambda) : 615 ms
式木から取り出す方は、変数名を文字列で渡した方に比べて、300倍以上遅い。式木の評価を遅延したとしても、ほんの少しの改善にしかならない。式木の構築は思っていた以上に高コストみたいだ。
コードの読みやすさとか、リファクタリングのしやすさを重視していたけど、パフォーマンスも気をつけるべきだったな。上記サンプルの場合、変数名を文字列で渡す方法でも、コードの品質は十分。むしろ、式木使うのはやりすぎか。反省。
皆さん、やりすぎにはくれぐれもご注意を。