2017-02-09 12 views
3

Expressions実際には同じではありませんが、そうでなければなりません。彼らは少し細部に違いがあります。私はExpressionsにはかなり新しいですが、これはかなり経験豊富なプレイヤーにとっても混乱を招く可能性があると思います。私はIQueryable.Where()のパラメータとしてExpressionを使用するようにいくつかのデータを処理するコードをリファクタリングしました。私が見る限り、それは機能的に同等です。式はほぼ同じですが、そのうちの1つが動作していません

私はここでうまく働いた元のコードを、持っている、と完全に機能的な表現を生成します。

private Expression<Func<T, bool>> StringPropertyContains<T>(string propertyName, string value) 
{ 
    if (string.IsNullOrWhiteSpace(propertyName)) 
    { 
     throw new ArgumentNullException(nameof(propertyName)); 
    } 

    var param = Expression.Parameter(typeof(T)); 

    MemberExpression member = null; 
    if (propertyName.Contains('/')) 
    { 
     var splittedPropertyName = propertyName.Split('/'); 

     var propertyInfo = this.GetPropertyInfo(typeof(T), splittedPropertyName.First()); 
     member = Expression.MakeMemberAccess(param, propertyInfo); 

     for (int i = 1; i < splittedPropertyName.Length; i++) 
     { 
      if (propertyInfo.PropertyType.IsInterface) 
      { 
       //specifically for IActorWithExtraDetails -> reason to refactor 
       if (typeof(IActor).IsAssignableFrom(propertyInfo.PropertyType) && typeof(IActor).GetProperties().FirstOrDefault(pi => pi.Name.Equals(splittedPropertyName[i], StringComparison.OrdinalIgnoreCase)) != null) 
       { 
        propertyInfo = this.GetPropertyInfo(typeof(IActor), splittedPropertyName[i]); 
       } 
       else 
       { 
        propertyInfo = this.GetPropertyInfo(propertyInfo.PropertyType, splittedPropertyName[i]); 
       } 
      } 
      else 
      { 
       propertyInfo = this.GetPropertyInfo(propertyInfo.PropertyType, splittedPropertyName[i]); 
      } 

     } 

     member = Expression.MakeMemberAccess(member, propertyInfo); 
    } 
    else 
    { 
     var propertyInfo = this.GetPropertyInfo(typeof(T), propertyName); 

     member = Expression.MakeMemberAccess(param, propertyInfo); 
    } 

    var constant = Expression.Constant(value, typeof(string)); 
    var methodInfo = typeof(string).GetMethod("Contains", new Type[] { typeof(string) }); 
    var body = Expression.Call(member, methodInfo, constant); 

    return Expression.Lambda<Func<T, bool>>(body, param); 
} 

は、これはIQueryableDebugViewプロパティにどのように見えるかです:ここでは

.Lambda #Lambda2<System.Func`2[AccessManagement.Model.Application,System.Boolean]>(AccessManagement.Model.Application $var1) 
{ 
    .Call ($var1.Name).Contains("hive") 
} 

は、新しいですリファクタリングされたコードを独自のメソッドに移動:

これは、リファクタリングメソッドからの発現がDebugViewでどのように見えるかです:

.Lambda #Lambda2<System.Func`2[AccessManagement.Model.Application,System.Boolean]>(AccessManagement.Model.Application $var1) 
{ 
    .Call ($var2.Name).Contains("hive") 
} 

一つだけ違いがあります。ご覧のとおり、$var2があります。$var1ではありません。この変数は、式ツリー全体には存在しません。理由は分かりませんが、他のすべてが同じなので、それが問題だと思います。他の違いは、Expressionの処理がmember.RuntimeMethodInfo.base.m_cachedData(デバッグビューのパス)にキャッチされている場合です。あなたの最初のコードスニペットで

+1

最初のコードスニペットは 'param'式を使用し、2番目のコードスニペットはそれを無視します。 –

+0

@LucasTrzesniewski申し訳ありません!ひどい間違い、私はリファクタリングされた式の処理を追加するのを忘れていましたが、 'param'は2番目のスニペットの最初のメソッドの最後の行に使われています – Qerts

+0

私が言ったことを誤解していると思います:)このコードを見てください' Expression.MakeMemberAccess Expression.Parameter(baseType)、propertyInfo) 'のように、' Expression.Parameter(baseType) 'を' param'への参照で置き換えます。これは 'Expression.Lambda'に渡すものと同じものです。ここでは第2の無関係のパラメータを作成しています。これは '$ var2'の由来です。 –

答えて

2

、あなたはパラメータを宣言している:

var param = Expression.Parameter(typeof(T)); 

これは、ラムダパラメータとして使用され、ここではコードで使用されています

Expression.MakeMemberAccess(param, propertyInfo); 

(これは実際には2回呼ばれる)。したがってコードはラムダに渡されるパラメータを利用します。 2番目のコードスニペットで


、あなたはまだラムダへのパラメータとしてparamを使用しているが、その後、あなたはどこにでもラムダ本体では使用しないでください。

あなたは代わりに、これを呼び出している:

Expression.MakeMemberAccess(Expression.Parameter(baseType), propertyInfo) 

Expression.Parameter(baseType)がそれに割り当てられた実際の値を取得することはありません秒と無関係変数を、作成すること。ここではparamのリファレンスを使用してください。

これは、$var2の由来です。 $var1paramの参照です。

次回はオーバーロードを使用することを検討してください。これにより、デバッグのためにパラメータに名前を付けることができます。それは理由を考えるのがより簡単になるでしょう。

関連する問題