2012-10-27 10 views
6


का उपयोग कर परिचय वस्तु बनानेगतिशील रूप से प्रतिबिंब, श्रृंखलित तरीकों और लैम्ब्डा भाव

मेरा आवेदन विधि श्रृंखलन का उपयोग कर तो यह उत्पन्न होते हैं और इसलिए तरह कॉन्फ़िगर किया गया है एक वस्तु को दर्शाता है:

var car = new Car("Ferrari").Doors(2).OtherProperties(x = x.Color("Red")); 


समस्या

मेरे पास एक आवश्यकता है टी को इस ऑब्जेक्ट को रनटाइम पर गतिशील रूप से उत्पन्न करने के लिए - कॉन्फ़िगरेशन के लिए जरूरी जंजीर विधियों को रनटाइम पर निर्धारित किया जाएगा ताकि फ्लाई पर सब कुछ गतिशील रूप से इकट्ठा किया जाना चाहिए। मैंने new Car("Ferrari", 2, "Red") जैसी सरल वस्तुओं को बनाने के लिए अतीत में प्रतिबिंब का उपयोग किया है - मैं इसके साथ अच्छा हूं - लेकिन लैम्बडा अभिव्यक्तियों वाले पैरामीटर के साथ कभी भी कुछ भी नहीं - पैरामीटर के रूप में इन दो कारकों में वास्तव में मुझे अटक गया है। मैंने अभिव्यक्ति के पेड़ों में देखा है और मानते हैं कि यह गतिशील अभिव्यक्ति पैरामीटर बनाने के लिए समाधान का हिस्सा है, लेकिन आधार वस्तु और अतिरिक्त जंजीर विधियों को बनाने के लिए प्रतिबिंब के साथ एक साथ सिलाई करने के तरीके को पूरी तरह से अटकने की कोशिश कर रहा हूं।


धन्यवाद और समय देने के लिए मेरी समस्या पर और किसी भी मार्गदर्शन या जानकारी प्रदान करने में सक्षम हो सकता है देखने के लिए के लिए प्रशंसा

अग्रिम में।


अद्यतन: निष्कर्ष dasblinkenlight और उनके जवाब के लिए जॉन स्कीट को

बहुत धन्यवाद। मैंने dasblinkenlight के जवाब को चुना क्योंकि उसके कोड नमूने ने मुझे बंद कर दिया और तुरंत चल रहा था। विधि श्रृंखला के लिए मैंने मूल रूप से स्वीकार्य उत्तर में एक ही लूपिंग दृष्टिकोण का उपयोग किया है, इसलिए मैं उस कोड को दोहराना नहीं चाहूंगा लेकिन नीचे दिया गया कोड है जिसे मैंने गतिशील रूप से अभिव्यक्ति वृक्ष विधि कॉल को क्रिया प्रतिनिधियों में परिवर्तित करने के लिए लिखा था जिसे बाद में उल्लिखित Invoke() के रूप में निष्पादित किया जा सकता था dasblinkenlight के जवाब में। यह, जैसा कि जॉन ने इंगित किया था वास्तव में समस्या का क्रूक्स था।

विधि मेटा डेटा स्टोर करने के लिए सहायक वर्ग।

public struct Argument 
    { 
     public string TypeName; 
     public object Value; 
    } 

public class ExpressionTreeMethodCall 
{ 
    public string MethodName { get; set; } 
    public IList<Argument> Arguments { get; set; } 

    public ExpressionTreeMethodCall() 
    { 
     Arguments = new List<Argument>(); 
    } 
} 


एक लैम्ब्डा अभिव्यक्ति विधि कॉल को इकट्ठा और फिर एक कार्रवाई प्रतिनिधि कहीं और निष्पादित किया जाना है (मेरे मामले में Invoke() को तर्क के रूप में पारित कर दिया) के रूप में यह वापस जाने के लिए स्टेटिक विधि।

public static Action<T> ConvertExpressionTreeMethodToDelegate<T>(ExpressionTreeMethodCall methodData) 
    {    
     ParameterExpression type = Expression.Parameter(typeof(T)); 

     var arguments = new List<ConstantExpression>(); 
     var argumentTypes = new List<Type>(); 

     foreach (var a in methodData.Arguments) 
     { 
      arguments.Add(Expression.Constant(a.Value)); 
      argumentTypes.Add(Type.GetType(a.TypeName)); 
     } 

     // Creating an expression for the method call and specifying its parameter. 
     MethodCallExpression methodCall = Expression.Call(type, typeof(T).GetMethod(methodData.MethodName, argumentTypes.ToArray()), arguments); 

     return Expression.Lambda<Action<T>>(methodCall, new[] { type }).Compile(); 
    } 

उत्तर

2

आप दो अलग-अलग समस्याओं का सामना कर रहे हैं।

मान लीजिए कि आप निम्नलिखित जानकारी उपलब्ध है कि दो:

  • एक ConstructorInfo श्रृंखला में पहला आइटम का प्रतिनिधित्व (निर्माता)
  • निर्माता के मानकों का प्रतिनिधित्व वस्तुओं की एक सरणी
  • MethodInfo की एक सरणी ऑब्जेक्ट्स - प्रत्येक जंजीर फ़ंक्शन के लिए
  • प्रत्येक श्रृंखलाबद्ध फ़ंक्शन के पैरामीटर का प्रतिनिधित्व करने वाले ऑब्जेक्ट एरे की एक सरणी

फिर परिणाम के निर्माण की प्रक्रिया इस प्रकार दिखाई देगा:

ConstructorInfo constr = ... 
object[] constrArgs = ... 
MethodInfo[] chainedMethods = ... 
object[][] chainedArgs = ... 
object res = constr.Invoke(constrArgs); 
for (int i = 0 ; i != chainedMethods.Length ; i++) { 
    // The chaining magic happens here: 
    res = chainedMethods[i].Invoke(res, chainedArgs[i]); 
} 

एक बार पाश खत्म हो गया है, अपने res कॉन्फ़िगर वस्तु में शामिल है।

उपर्युक्त कोड मानता है कि जंजीर विधियों के बीच कोई सामान्य विधि नहीं है; यदि कुछ विधियां जेनेरिक होती हैं, तो आपको Invoke पर कॉल करने से पहले एक सामान्य विधि का एक कॉल करने योग्य उदाहरण बनाने में एक अतिरिक्त कदम की आवश्यकता होगी।

अब चलो लैम्बडा देखें। विधि को पारित लैम्ब्डा के प्रकार के आधार पर, आपको एक विशेष हस्ताक्षर के साथ एक प्रतिनिधि को पास करने की आवश्यकता है। कॉल करने योग्य प्रतिनिधियों में तरीकों को बदलने के लिए आपको System.Delegate कक्षा का उपयोग करने में सक्षम होना चाहिए। आपको उन समर्थन विधियों को बनाने की आवश्यकता हो सकती है जो आपको आवश्यक प्रतिनिधियों को लागू करते हैं। यह कहना मुश्किल है कि कैसे सटीक तरीकों को देखे बिना आपको प्रतिबिंब के माध्यम से कॉल करने में सक्षम होना चाहिए। आपको अभिव्यक्ति पेड़ के लिए जाना पड़ सकता है, और उन्हें संकलित करने के बाद Func<...> उदाहरण प्राप्त करें।x.Color("Red") की कॉल इस प्रकार दिखाई देगा:

Expression arg = Expression.Parameter(typeof(MyCarType)); 
Expression red = Expression.Constant("Red"); 
MethodInfo color = typeof(MyCarType).GetMethod("Color"); 
Expression call = Expression.Call(arg, color, new[] {red}); 
var lambda = Expression.Lambda(call, new[] {arg}); 
Action<MyCarType> makeRed = (Action<MyCarType>)lambda.Compile(); 
+0

बहुत धन्यवाद दास .. तो अपने प्रदान की कोड का उपयोग कर - "makeRed" एक्शन वस्तु "chainedArgs" में संग्रहित किया जा सकता है और मार डाला "आह्वान()" के दौरान कॉल के दौरान चेनिंग पाश? – mmacneil007

+0

@ mmacneil007 बिल्कुल, यह विचार है। 'एक्शन ' एक प्रतिनिधि है जिसे अपने संबंधित विधि (आपके मामले में, 'अन्य प्रॉपर्टीज' 'के रूप में पारित करने के लिए, सरणी के' जंजीर आर्ट्स 'सरणी के अंदर एक सरणी में संग्रहीत किया जा सकता है। – dasblinkenlight

+0

बढ़िया, मुझे विश्वास है कि इसने मुझे सही रास्ते पर स्थापित किया है। अभी भी अभिव्यक्ति के पेड़ों के चारों ओर अपने सिर को लपेटना है, लेकिन मुझे लगता है कि आपके द्वारा यहां उल्लिखित दृष्टिकोण को चाल चलनी चाहिए। एक बार फिर धन्यवाद! – mmacneil007

1

लेकिन कभी लैम्ब्डा भाव युक्त के रूप में मानकों

खैर, श्रृंखलित तरीकों बिट कर रहे हैं श्रृंखलित तरीकों के साथ कुछ भी। यह सिर्फ कई बार प्रतिबिंब का उपयोग करने का मामला है।कॉल लक्ष्य के रूप में foo के मूल्य का उपयोग प्रतिबिंब के माध्यम से विधि आह्वान, और याद foo

  • की घोषित प्रकार से

    • प्राप्त विधि X: श्रृंखला के लिए एक साथ

      foo.X().Y() 
      

      आप की जरूरत है नतीजा (उदाहरण के लिए tmp)

    • के घोषित प्रकार से के प्रकार से विधि प्राप्त करें(MethodInfo.ReturnType देखें)
    • प्रतिबिंब के माध्यम से विधि आह्वान कॉल लक्ष्य

    लैम्ब्डा अभिव्यक्ति कठिन है के रूप में पिछले परिणाम (tmp) का उपयोग कर - यह वास्तव में कैसे आप अभिव्यक्ति प्रदान की जानी करने जा रहे हैं पर निर्भर करता है पहली जगह में। एक उचित रूप से मनमानी अभिव्यक्ति निष्पादित करने के लिए एक प्रतिनिधि का निर्माण expression trees का उपयोग करना और LambdaExpression.Compile पर कॉल करना बहुत कठिन नहीं है, लेकिन आपको यह जानने की आवश्यकता है कि आप क्या कर रहे हैं।

    • लागू श्रृंखलित विधियों, और
    • तरीकों कि lambdas लेने के रूप में अलग से दो के साथ

    आइए सौदा पैरामीटर लागू:

  • संबंधित मुद्दे