2010-09-22 9 views
7

मूल रूप से मैं इस तरह एक कहा जाता विधि के मापदंडों के मूल्यों को प्राप्त करना चाहते हैं:पैरामीटर

var x = 1; 
var a = 2; 
var b = 3; 
Do<HomeController>(o => o.Save(x, "Jimmy", a+b+5, Math.Sqrt(81))); 

public static void Do<T>(Expression<Action<T>> expression) where T : Controller 
{ 
    // get the values 1,Jimmy,10,9 here 
} 

उत्तर

17

ठीक है, तुम, अभिव्यक्ति और गहराई में जाने की जरूरत होगी MethodCallExpression लगता है , और फिर इसके लिए तर्क देखें। ध्यान दें कि हमारे पास o का मान नहीं है, इसलिए हमें यह मानना ​​होगा कि विधि के तर्क उस पर भरोसा नहीं करते हैं। इसके अलावा हम अभी भी यह मान रहे हैं कि लैम्ब्डा अभिव्यक्ति सिर्फ MethodCallExpression पर निर्भर करती है?

संपादित करें: ठीक है, यहां एक संपादित संस्करण है जो तर्कों का मूल्यांकन करता है। हालांकि, यह मानता है कि आप वास्तव में तर्कों के भीतर लैम्ब्डा अभिव्यक्ति पैरामीटर का उपयोग कर रहे हैं (जो new object[1] है - यह प्रभावी रूप से एक शून्य पैरामीटर प्रदान कर रहा है)। इस अर्थ में कि यह मूल रूप नहीं है -

using System; 
using System.Linq.Expressions; 

class Foo 
{ 
    public void Save(int x, string y, int z, double d) 
    { 
    } 
} 

class Program 
{ 
    static void Main() 
    { 
     var x = 1; 
     var a = 2; 
     var b = 3; 
     ShowValues<Foo>(o => o.Save(x, "Jimmy", a + b + 5, Math.Sqrt(81))); 
    } 

    static void ShowValues<T>(Expression<Action<T>> expression) 
    { 
     var call = expression.Body as MethodCallExpression; 
     if (call == null) 
     { 
      throw new ArgumentException("Not a method call"); 
     } 
     foreach (Expression argument in call.Arguments) 
     { 
      LambdaExpression lambda = Expression.Lambda(argument, 
                 expression.Parameters); 
      Delegate d = lambda.Compile(); 
      object value = d.DynamicInvoke(new object[1]); 
      Console.WriteLine("Got value: {0}", value); 
     } 
    } 
} 
+2

@ ओमु: लेकिन क्या आप वास्तव में कोड चाहते हैं जो टूट जाएगा - केवल निष्पादन समय पर - यदि आपने * किसी भी * लैम्ब्डा अभिव्यक्ति के अन्य रूप का उपयोग किया है? यदि आप हमेशा * कॉलिंग कॉल करने जा रहे हैं, तो क्यों न केवल पैरामीटर को सीधे पास करें? –

+0

@ जोन स्कीट, मैं विभिन्न पैरामीटर के साथ कई अन्य तरीकों को बुला रहा हूं, मुझे नहीं पता था कि मैं केवल स्थिर मूल्यों के साथ कॉल कर सकता हूं, मुझे लगता है कि मुझे इस और – Omu

+0

@Omu की जांच करनी होगी: बिना किसी स्पष्टीकरण वाले प्रश्न में डंपिंग कोड के साथ यह समस्या है। मैंने कहा * यह भंगुर था, और जैसे ही आपने किसी अन्य पैटर्न का उपयोग करना शुरू कर दिया था ... –

4

के रूप में जॉन ने कहा कि आप अगर अभिव्यक्ति एक MethodCallExpression

class Program 
{ 
    static void Main(string[] args) 
    { 
     Program.Do<Controller>(c => c.Save(1, "Jimmy")); 
    } 

    public static void Do<T>(Expression<Action<T>> expression) where T : Controller 
    { 
     var body = expression.Body as MethodCallExpression; 
     if (body != null) 
     { 
      foreach (var argument in body.Arguments) 
      { 
       var constant = argument as ConstantExpression; 
       if (constant != null) 
       { 
        Console.WriteLine(constant.Value); 
       } 
      } 
     } 
    } 
} 

public class Controller 
{ 
    public void Save(int id, string name) 
    { 
    } 
} 
+0

मुझे इस तथ्य से बहुत अच्छा लगता है कि हमारा डेमो कोड लगभग समान है :) –

+0

@ जोन, मेरे पास कोई त्रुटि जांचने का समय नहीं था :( –

+0

हाँ, यह किसी भी तरह से भंगुर है। मुझे संदेह है कि यह सबसे अच्छा तरीका है, लेकिन हम अधिक संदर्भ नहीं है। –

2

यहाँ है देखने के लिए जाँच कर सकते हैं कुछ कोड है कि किसी भी अभिव्यक्ति के साथ काम करने के लिए डिज़ाइन किया गया है मान लें कि आप एक विधि-कॉल अभिव्यक्ति में गुजर रहे हैं। हालांकि, यह पूरा नहीं हुआ है। आपको बाकी को भरना होगा।

public static IEnumerable<object> ExtractConstants<T>(
     Expression<Action<T>> expression) 
{ 
    return extractConstants(expression); 
} 
private static IEnumerable<object> extractConstants(Expression expression) 
{ 
    if (expression == null) 
     yield break; 

    if (expression is ConstantExpression) 
     yield return ((ConstantExpression) expression).Value; 

    else if (expression is LambdaExpression) 
     foreach (var constant in extractConstants(
       ((LambdaExpression) expression).Body)) 
      yield return constant; 

    else if (expression is UnaryExpression) 
     foreach (var constant in extractConstants(
       ((UnaryExpression) expression).Operand)) 
      yield return constant; 

    else if (expression is MethodCallExpression) 
    { 
     foreach (var arg in ((MethodCallExpression) expression).Arguments) 
      foreach (var constant in extractConstants(arg)) 
       yield return constant; 
     foreach (var constant in extractConstants(
       ((MethodCallExpression) expression).Object)) 
      yield return constant; 
    } 

    else 
     throw new NotImplementedException(); 
} 

मामले कि आपने उल्लेख किया है के लिए, यह पहले से ही काम करता है:

// Prints: 
// Jimmy (System.String) 
// 1 (System.Int32) 
foreach (var constant in Ext.ExtractConstants<string>(
     str => Console.WriteLine("Jimmy", 1))) 
    Console.WriteLine("{0} ({1})", constant.ToString(), 
            constant.GetType().FullName); 

अधिक जटिल लैम्ब्डा भाव कि अभिव्यक्ति नोड्स के अन्य प्रकार को रोजगार के लिए, आप संवर्द्धित ऊपर कोड का विस्तार करना होगा।

  • ओपन डीबगर में घड़ी खिड़की
  • expression चर और उसके प्रकार
  • पर
  • देखो आवश्यक कोड को संभालने के लिए जोड़ें: हर बार जब आप इसका इस्तेमाल करते हैं और यह एक NotImplementedException फेंकता है, यहाँ मैं क्या कर रहा है उस अभिव्यक्ति प्रकार

समय के साथ विधि अधिक से अधिक पूर्ण हो जाएगी।

2

मेरा सार्वभौमिक उत्तर नीचे है। मुझे उम्मीद है कि यह आपको और किसी और की मदद करेगा।

var dict = new Dictionary<string, object>(); 
var parameterExpressions = methodCallExpr.Arguments; 
foreach (var param in method.GetParameters()) 
{ 
    var parameterExpression = parameterExpressions[counter]; 
    var paramValueAccessor = Expression.Lambda(parameterExpression); 
    var paramValue = paramValueAccessor.Compile().DynamicInvoke(); 
    dict[param.Name] = paramValue; 
} 
0
public override IQueryable<Image> FindAll(System.Linq.Expressions.Expression<Func<Image, dynamic>> Id) 
     { 
      dynamic currentType = Id.Parameters[0]; 
      var id = currentType.Type.GUID; 
      var result = (_uniwOfWork as UnitOfWork).uspGetImages(id.ToString()); 
      return FindAll(); 
     } 

उपयोग कीवर्ड गतिशील।