سؤال

لذا دعونا نقول لدي التعبير التالي في C#:

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

كيف يمكنني سحب إشارة إلى فو?

هل كانت مفيدة؟

المحلول

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);

نصائح أخرى

كان عندي نفس المشكلة ولكن إلى حد ما أكثر تعقيدا ، دارين ديميتروف الجواب أعطاني بداية جيدة.أنا ما بعد النتائج هنا ، على الرغم من حقيقة أن هذا هو "قديم" السؤال .


حالة 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'

...النتائج في مختلف شجرة التعبير.ملاحظة مرجع فارغة في أسفل اليسار:

.                                    +====================+
.                                    |  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.بدلا من ذلك, يمكنك التوقف عن تنازلي عندما تصل إلى مرجع فارغة.التالي, يمكنك استرداد الجذر مرجع كائن على النحو التالي:

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

إن "يصعد" مرحلة من هناك فصاعدا هو نفسه كما كان من قبل.


هناك بالتأكيد أكثر الحالات (مثل اسمه المعلمات الكائن الجذر) ، ولكن آمل أن الآن لدي الفكرة الأساسية عبر لذا سأقطع هنا.

وهناك حل أسهل:

var pExpression = ((MemberExpression)expr.Body);
var bindingObject = Expression.Lambda(((MemberExpression)pExpression.Expression)).Compile().DynamicInvoke();

شكرا، staks - مثال بك ساعدني كثيرا! لذلك أنا ترغب في المساهمة مع بعض بالإضافة إلى الحالة الأولى:

لاستخراج القيم من الأساليب، ينبغي لأحد أن يحل محل كود:

// 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))

وهذا هو ما يمكنني استخدامها في وحدة الاختبارات:

 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;
  }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top