43

したがって、C#で次の式があるとします。

Expression<Func<string>> expr = () => foo.Bar;

fooへの参照を引き出すにはどうすればよいですか?

4

5 に答える 5

51

私は同じ問題を抱えていましたが、やや複雑で、Darin Dimitrovの答えが良いスタートを切りました。これは「古い」質問であるという事実にもかかわらず、ここに結果を投稿します。


ケース 1: ルート オブジェクトがインスタンス メンバーである

    this.textBox.Text    // where 'this' has type 'Form'

... 次の式ツリーと同等です。

.                                    +====================+
.                                    |  MemberExpression  |
.                                    +====================+
#                                      |                |
#                          .Expression |                | .Member
#                                      v                v
.                    +------------------+              +------------+
.                    | MemberExpression |              | MemberInfo |
.                    +------------------+              +------------+
#                      |              |                 .Name = "Text"
#          .Expression |              | .Member         .MemberType = Property
#                      v              v
.   +--------------------+          +------------+
.   | ConstantExpression |          | MemberInfo |
.   +--------------------+          +------------+
#    .Value = this                   .Name = "textBox"
#    .Type  = typeof(Form)           .MemberType = Field

この式ツリーで実際にオブジェクト参照を取得する唯一の場所は からConstantExpressionです。これにより、 への参照を取得できますthis。したがって、このツリーで任意のオブジェクト参照を取得するための基本的な考え方は次のとおりです。

  1. ノード.Expressionに到達するまで、軸に沿って式ツリーに降ります。ConstantExpression

  2. そのノードの.Valueプロパティを取得します。これはルート オブジェクト参照です (つまりthis、上記の例では)。

  3. リフレクションと式ツリーのMemberInfoノードを使用して、オブジェクト参照を取得し、式ツリーを「上に」戻します。

これを示すコードを次に示します。

Expression expr = ...;   // <-- initially set to the expression tree's root

var memberInfos = new Stack<MemberInfo>();

// "descend" toward's the root object reference:
while (expr is MemberExpression)
{
    var memberExpr = expr as MemberExpression;
    memberInfos.Push(memberExpr.Member);
    expr = memberExpr.Expression
}

// fetch the root object reference:
var constExpr = expr as ConstantExpression;
var objReference = constExpr.Value;

// "ascend" back whence we came from and resolve object references along the way:
while (memberInfos.Count > 0)  // or some other break condition
{
    var mi = memberInfos.Pop();
    if (mi.MemberType == MemberTypes.Property)
    {
        objReference = objReference.GetType()
                                   .GetProperty(mi.Name)
                                   .GetValue(objReference, null);
    }
    else if (mi.MemberType == MemberTypes.Field)
    {
        objReference = objReference.GetType()
                                   .GetField(mi.Name)
                                   .GetValue(objReference);
    }
}

ケース 2: ルート オブジェクトが静的クラス メンバーである

    Form.textBox.Text    // where 'textBox' is a static member of type 'Form'

...結果は異なる式ツリーになります。左下の null 参照に注意してください。

.                                    +====================+
.                                    |  MemberExpression  |
.                                    +====================+
#                                      |                |
#                          .Expression |                | .Member
#                                      v                v
.                    +------------------+              +------------+
.                    | MemberExpression |              | MemberInfo |
.                    +------------------+              +------------+
#                      |              |                 .Name = "Text"
#          .Expression |              | .Member         .MemberType = Property
#                      v              v
.                     null          +------------+
.                                   | MemberInfo |
.                                   +------------+
#                                   .Name = "textBox"
#                                   .MemberType = Field
#                                   .DeclaringType = typeof(Form)

ここでは、 を待って「下降」フェーズを停止することはできませんConstantExpression。代わりに、null 参照に到達すると下降を停止します。次に、ルート オブジェクト参照を次のように取得します。

var mi = memberInfos.Pop();
objReference = mi.DeclaringType
                 .GetField(member.Name, BindingFlags.Static)  // or .GetProperty!
                 .GetValue(null);

そこから先の「上昇」フェーズは以前と同じです。


確かにもっと多くのケース (ルート オブジェクトとしての名前付きパラメーターなど) がありますが、ここまでで基本的な考え方が理解できたと思いますので、ここで中断します。

于 2010-10-17T16:30:20.637 に答える
41
Expression<Func<string>> expr = () => foo.Bar;
var me = (MemberExpression)((MemberExpression)expr.Body).Expression;
var ce = (ConstantExpression)me.Expression;
var fieldInfo = ce.Value.GetType().GetField(me.Member.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
var value = (Foo)fieldInfo.GetValue(ce.Value);
于 2009-10-23T13:28:06.573 に答える
0

これは私が単体テストで使用するものです:

 internal static INotifyPropertyChanged SubModel < T, TProperty > (T model, Expression < Func < T, TProperty >> pickProperty) where T: INotifyPropertyChanged {
   MemberExpression memberExpression = (MemberExpression) pickProperty.Body;
   ParameterExpression parameterExpression = pickProperty.Parameters[0];
   Expression mem = memberExpression.Expression;
   var delegateType = typeof(Func < , > ).MakeGenericType(typeof(T), mem.Type);
   LambdaExpression lambdaExpression = Expression.Lambda(delegateType, mem, parameterExpression);
   object subModel = lambdaExpression.Compile().DynamicInvoke(model);
   return subModel as INotifyPropertyChanged ? ? model;
  }
于 2014-06-02T10:18:25.340 に答える
0

ありがとう、スタックス - あなたの例は私を大いに助けてくれました! だから私は最初のケースにいくつか追加して貢献したいと思います:

メソッドから値を抽出するには、コードを置き換える必要があります。

// fetch the root object reference:
var constExpr = expr as ConstantExpression;
var objReference = constExpr.Value;

コードで:

    var newExpression = expr as NewExpression;
    if (newExpression != null)
    {                
        return newExpression.Constructor.Invoke(newExpression.Arguments.Select(GetObjectValue).ToArray());
    }

    var methodCallExpr = expr as MethodCallExpression;
    if (methodCallExpr != null)
    {
        var value = methodCallExpr.Method.Invoke(methodCallExpr.Object == null
                                                                 ? null
                                                                 : GetObjectValue(methodCallExpr.Object),
methodCallExpr.Arguments.Select(GetObjectValue).ToArray());
                    return value;
    }

    // fetch the root object reference:
    var constExpr = expr as ConstantExpression;
    if (constExpr == null)
    {
         return null;
    }
    var objReference = constExpr.Value;
    // ... the rest remains unchanged

そうすれば、次のような式から値を抽出できます。

aInstane.MethodCall(anArgument1, anArgument2) or
AType.MethodCall(anArgument1, anArgument2) or
new AType().MethodCall(anArgument1, aInstane.MethodCall(anArgument2, anArgument3))
于 2012-11-27T06:31:20.117 に答える