2011-10-18 7 views
12

यह कोड का उपयोग करके सबसे अच्छा समझाया गया है। मेरे पास एक सामान्य वर्ग है जिसमें एक विधि है जो एक पूर्णांक देता है। यहाँ समझाने का प्रयोजनों के लिए एक सरल संस्करण ...अभिव्यक्ति कैसे बनाएं। लैम्ब्डा जब रनटाइम तक किसी प्रकार को ज्ञात नहीं है?

public class Gen<T> 
{ 
    public int DoSomething(T instance) 
    { 
     // Real code does something more interesting! 
     return 1; 
    } 
} 

रनटाइम पर मैं प्रतिबिंब का उपयोग कुछ के प्रकार की खोज करने और फिर उस विशिष्ट प्रकार के लिए मेरी जनरल वर्ग का एक उदाहरण बनाना चाहते है। यही कारण है कि ... काफी आसान है और इस तरह से किया है

Type fieldType = // This is the type I have discovered 
Type genericType = typeof(Gen<>).MakeGenericType(fieldType); 
object genericInstance = Activator.CreateInstance(genericType); 

मैं अब एक अभिव्यक्ति है कि एक पैरामीटर के रूप में सामान्य प्रकार का एक उदाहरण ले जाएगा बनाने के लिए चाहते हैं और फिर उस प्रकार के DoSomething प्रणाली को बुलाती है। तो मैं अभिव्यक्ति प्रभावी रूप से इस प्रदर्शन करने के लिए चाहते हैं ...

int answer = genericInstance.DoSomething(instance); 

... सिवाय मैं रनटाइम पर बाद में कुछ बिंदु तक 'उदाहरण' की जरूरत नहीं है और genericInstance उत्पन्न प्रकार के रूप में ऊपर देखा जा सकता है। लैम्ब्डा बनाने के लिए इस प्रकार है पर मेरे प्रयास ...

MethodInfo mi = genericType.GetMethod("DoSomething", 
             BindingFlags.Instance | BindingFlags.Public); 

var p1 = Expression.Parameter(genericType, "generic"); 
var p2 = Expression.Parameter(fieldType, "instance"); 

var x = Expression.Lambda<Func<genericType, fieldType, int>> 
      (Expression.Call(p1, mi, p2), 
      new[] { p1, p2 }).Compile(); 

... ताकि बाद में मैं कुछ इस तरह से कॉल करने की कर सकते हैं ...

int answer = x(genericInstance, instance); 
बेशक

, आप उदाहरण पैरामीटर के साथ Func प्रदान नहीं कर सकता है और इसलिए मुझे नहीं पता कि लैम्ब्डा पीढ़ी को पैरामीटर कैसे करें। कोई विचार?

उत्तर

18

मुझे लगता है कि तुम सिर्फ Expression.Lambda कि एक सामान्य रूप में नहीं बल्कि तो एक प्रकार के रूप में प्रतिनिधि प्रकार लेता है का प्रयोग करेंगे, और जैसे आप Gen<> साथ हैं तेज़ी से अपने समारोह बनाने के लिए:

MethodInfo mi = genericType.GetMethod("DoSomething", 
           BindingFlags.Instance | BindingFlags.Public); 

var p1 = Expression.Parameter(genericType, "generic"); 
var p2 = Expression.Parameter(fieldType, "instance"); 
var func = typeof (Func<,,>); 
var genericFunc = func.MakeGenericType(genericType, fieldType, typeof(int)); 
var x = Expression.Lambda(genericFunc, Expression.Call(p1, mi, p2), 
       new[] { p1, p2 }).Compile(); 

यह एक वापस आ जाएगी दृढ़ता से टाइप किए गए Func की बजाय प्रतिनिधि, लेकिन यदि आवश्यक हो तो आप इसे निश्चित रूप से कास्ट कर सकते हैं (और यदि आपको पता नहीं है कि आप क्या कर रहे हैं) और गतिशील रूप से DynamicInvoke का उपयोग करके इसे गतिमान रूप से आमंत्रित करें।

int answer = (int) x.DynamicInvoke(genericInstance, instance); 

संपादित:

एक अच्छा विचार है कि वास्तव में काम करता है। दुर्भाग्य से मैं दृढ़ता से टाइप किए गए संकलित लैम्ब्डा का उपयोग करना चाहता हूं। टाइप किए गए लैम्ब्डा की तुलना में डायनामिक इनवोक का उपयोग करना काफी धीमा है।

यह गतिशील चालान की आवश्यकता के बिना काम करता प्रतीत होता है।

var p1 = Expression.Parameter(genericType, "generic"); 
var p2 = Expression.Parameter(fieldType, "instance"); 
var func = typeof(Func<,,>); 
var genericFunc = func.MakeGenericType(genericType, fieldType, typeof(int)); 
var x = Expression.Lambda(genericFunc, Expression.Call(p1, mi, p2), new[] { p1, p2 }); 
var invoke = Expression.Invoke(x, Expression.Constant(genericInstance), Expression.Constant(instance)); 
var answer = Expression.Lambda<Func<int>>(invoke).Compile()(); 

संपादित 2:

एक बहुत सरलीकृत संस्करण:

Type fieldType = ;// This is the type I have discovered 
Type genericType = typeof(Gen<>).MakeGenericType(fieldType); 
object genericInstance = Activator.CreateInstance(genericType); 
MethodInfo mi = genericType.GetMethod("DoSomething", 
           BindingFlags.Instance | BindingFlags.Public); 
var value = Expression.Constant(instance, fieldType); 
var lambda = Expression.Lambda<Func<int>>(Expression.Call(Expression.Constant(genericInstance), mi, value)); 
var answer = lambda.Compile()(); 
+0

एक अच्छा विचार है जो वास्तव में काम करता है। दुर्भाग्य से मैं दृढ़ता से टाइप किए गए संकलित लैम्ब्डा का उपयोग करना चाहता हूं। टाइप किए गए लैम्ब्डा की तुलना में डायनामिक इनवोक का उपयोग करना काफी धीमा है। –

+0

क्या अभिव्यक्ति वृक्ष में चर को कैप्चर करना संभव है? मैं जेनेरिक इंस्टेंस को कैप्चर करने में मदद करूंगा क्योंकि यह कभी नहीं बदलेगा। –

+0

@ फिलहाइट एचएम, मैं देखता हूं। मुझे देखने दो कि मैं और क्या कर सकता हूं। – vcsjones

1

यह उत्तर केवल लागू होता है आप .NET 4.0 उपयोग कर रहे हैं।

आप object के बजाय genericInstancedynamic बनाने हैं, तो आप तो DoSomething विधि उस पर सीधे कॉल कर सकते हैं, और गतिशील भाषा क्रम आप के लिए सब कुछ का ख्याल रखना होगा।

class Type1 { 
    public int DoSomething() { return 1; } 
} 
class Type2 { 
    public int DoSomething() { return 2; } 
} 

static void TestDynamic() { 
    dynamic t1 = Activator.CreateInstance(typeof(Type1)); 
    int answer1 = t1.DoSomething(); // returns 1 

    dynamic t2 = Activator.CreateInstance(typeof(Type2)); 
    int answer2 = t2.DoSomething(); // returns 2 
} 

आप इस वर्ग संरचना (Gen<T>) रखने की जरूरत है, तो मैं इस तथ्य है कि आप संकलन समय पर प्रकार T पता नहीं है चारों ओर एक आसान तरीका नहीं दिख रहा। यदि आप प्रतिनिधि को कॉल करना चाहते हैं, तो आपको या तो संकलन समय पर अपना पूरा प्रकार पता होना चाहिए, या आपको पैरामीटर में ऑब्जेक्ट्स के रूप में पास करने की आवश्यकता है।

dynamic का उपयोग करके आप MethodInfo आदि प्राप्त करने की जटिलता को छिपाने के लिए प्राप्त करते हैं, और आपको उत्कृष्ट प्रदर्शन देता है। एक दोष बनाम DynamicInvoke जो मैं देखता हूं वह है कि मुझे विश्वास है कि आपको प्रत्येक कॉल साइट के लिए एक बार गतिशील कॉल को हल करने का प्रारंभिक ओवरहेड मिलता है। बाइंडिंग को कैश किया जाता है ताकि यदि आप इसे उसी प्रकार के ऑब्जेक्ट्स पर कॉल करते हैं तो वे दूसरी बार से बहुत तेजी से दौड़ते हैं।

0

object स्वीकार करना बेहतर है और convert को ज्ञात प्रकार के लिए उपयोग करना बेहतर है।

var model = new { A = new { B = 10L } }; 
string prop = "A.B"; 
var parameter = Expression.Parameter(typeof(object)); 
Func<object, long> expr = (Func<object, long>) Expression.Lambda(prop.Split('.').Aggregate<string, Expression>(Expression.Convert(parameter, model.GetType()), Expression.Property), parameter).Compile(); 
expr(model).Dump(); 

यह DynamicInvoke की अतिरिक्त लागत से बचा जाता है प्रतिनिधि के प्रकार के संकलन समय पर अज्ञात है जब:

यहाँ कैसे अज्ञात गहराई पर नाम से एक प्रॉपर्टी तक पहुंच निर्माण करने के लिए एक उदाहरण है,।

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