2009-02-19 18 views
10

मैं एक लम्बा अभिव्यक्ति का उपयोग करने के लिए देख रहा था ताकि घटनाओं को मजबूती से टाइप किया जा सके, लेकिन मध्य में श्रोता के साथ, उदाहरण के लिए निम्नलिखित वर्गोंलैम्बडा एक्सप्रेशन में घटनाक्रम - सी # कंपाइलर बग?

class Producer 
{ 
    public event EventHandler MyEvent; 
} 

class Consumer 
{ 
    public void MyHandler(object sender, EventArgs e) { /* ... */ } 
} 

class Listener 
{ 
    public static void WireUp<TProducer, TConsumer>(
     Expression<Action<TProducer, TConsumer>> expr) { /* ... */ } 
} 

एक घटना के रूप में तार हो जाएगा दिया:

Listener.WireUp<Producer, Consumer>((p, c) => p.MyEvent += c.MyHandler); 

हालांकि यह एक संकलक त्रुटि देता है:

CS0832: An expression tree may not contain an assignment operator

अब पहली बार इस उचित लगता है पर, विशेष रूप से reading the explanation about why expression trees cannot contain assignments के बाद । हालांकि, सी # वाक्य रचना के बावजूद, += नहीं एक काम, है यह, Producer::add_MyEvent विधि के लिए एक कॉल है के रूप में हम कोल इंडिया उत्पादन किया है कि से देख सकते हैं, तो हम सिर्फ घटना ऊपर तार सामान्य रूप से:

L_0001: newobj instance void LambdaEvents.Producer::.ctor() 
L_0007: newobj instance void LambdaEvents.Consumer::.ctor() 
L_000f: ldftn instance void LambdaEvents.Consumer::MyHandler(object, class [mscorlib]System.EventArgs) 
L_0015: newobj instance void [mscorlib]System.EventHandler::.ctor(object, native int) 
L_001a: callvirt instance void LambdaEvents.Producer::add_MyEvent(class [mscorlib]System.EventHandler) 

तो ऐसा लगता है कि यह एक कंपाइलर बग है क्योंकि यह असाइनमेंट की अनुमति नहीं दे रहा है, लेकिन कोई असाइनमेंट नहीं है, बस एक विधि कॉल है। या क्या मैं कुछ न कुछ भूल रहा हूं...?

संपादित करें:

कृपया ध्यान दें कि सवाल यह है कि "इस व्यवहार एक संकलक बग है?"। क्षमा करें अगर मैं जो पूछ रहा था उसके बारे में स्पष्ट नहीं था।

संपादित 2

Inferis 'जवाब, जहां वे कहते हैं पढ़ने के बाद "उस बिंदु पर + = काम माना जाता है" यह कुछ मतलब होता है, क्योंकि इस समय तक संकलक यकीनन नहीं है पता है कि यह सीआईएल में बदल जाएगा।

हालांकि मैं स्पष्ट विधि कॉल प्रपत्र लिखने के लिए अनुमति नहीं कर रहा हूँ:

Listener.WireUp<Producer, Consumer>(
    (p, c) => p.add_MyEvent(new EventHandler(c.MyHandler))); 

देता है:

CS0571: 'Producer.MyEvent.add': cannot explicitly call operator or accessor

तो, मुझे लगता है कि प्रश्न के नीचे आता है क्या += वास्तव में के संदर्भ में इसका मतलब है सी # घटनाओं। इसका मतलब है "इस घटना के लिए ऐड विधि को कॉल करें" या इसका मतलब यह है कि "इस घटना में अभी तक अनिर्धारित तरीके से जोड़ें"। यदि यह पूर्व है तो यह मुझे एक कंपाइलर बग होने लगता है, जबकि यदि यह बाद वाला है तो यह कुछ हद तक अनजान है लेकिन तर्कसंगत रूप से एक बग नहीं है। विचार?

+1

+ = का अर्थ है "दिए गए ईवेंट के लिए ईवेंट एक्सेसर को कॉल करें"। मुझे लगता है कि यह एक बग नहीं है, लेकिन थोड़ा कष्टप्रद और तर्कसंगत अनावश्यक प्रतिबंध है। –

उत्तर

5

spec में, अनुभाग 7.16.3, + = और - = ऑपरेटरों को "ईवेंट असाइनमेंट" कहा जाता है जो निश्चित रूप से इसे असाइनमेंट ऑपरेटर की तरह ध्वनि बनाता है। तथ्य यह है कि यह धारा 7.16 ("असाइनमेंट ऑपरेटर") के भीतर है एक बहुत बड़ा संकेत है :) उस बिंदु से, संकलक त्रुटि समझ में आता है।

हालांकि, मैं मानता हूं कि यह अत्यधिक प्रतिबंधित है क्योंकि यह अभिव्यक्ति वृक्ष के लिए लैम्ब्डा अभिव्यक्ति द्वारा दी गई कार्यक्षमता का प्रतिनिधित्व करने के लिए पूरी तरह संभव है।

I संदिग्ध भाषा डिजाइनर इस तरह की परिस्थितियों के खर्च पर "थोड़ा अधिक प्रतिबंधक लेकिन ऑपरेटर विवरण में अधिक संगत" दृष्टिकोण के लिए गए, मुझे डर है।

+0

जॉन - हाँ वह वहीं है जहां मैं बस जा रहा था (मेरा संपादन # 2 देखें)। इसलिए मुझे लगता है कि spec क्या इंगित कर रहा है कि + = घटनाओं के साथ "मेरे लिए ऐड विधि को कॉल करने के बजाय" ईवेंट को अभी तक अनिर्धारित तरीके से असाइन करें "। इस मामले में यह निराशाजनक होगा, लेकिन तकनीकी रूप से एक बग नहीं है। –

+0

मुझे नहीं लगता कि यह "अभी तक अपरिभाषित" है - यह विधि की पहचान कर सकता है और एक अभिव्यक्ति वृक्ष उत्सर्जित करता है जो इसे कॉल करता है। यह उचित व्यवहार होगा, सिवाय इसके कि यह एक ऑपरेटर सौंपेगा जो अभी भी - कड़ाई से बोल रहा है - एक असाइनमेंट ऑपरेटर। –

+0

ठीक है आपने मुझे विश्वास दिलाया है; मुझे लगता है कि यह तकनीकी रूप से एक असाइनमेंट ऑपरेटर है, जिसका अर्थ है कि यह एक बग नहीं है। लेकिन यह निराशाजनक है क्योंकि हम सभी जानते हैं * यह वास्तव में नीचे एक विधि कॉल है ... आह अच्छी तरह से: -एस –

1

+ = एक असाइनमेंट है, इससे कोई फर्क नहीं पड़ता कि यह क्या करता है (उदा। कोई ईवेंट जोड़ें)। पार्सर दृष्टिकोण से, यह अभी भी एक असाइनमेंट है।

आप

Listener.WireUp<Producer, Consumer>((p, c) => { p.MyEvent += c.MyHandler; }); 
+0

पार्सर इसे असाइनमेंट के रूप में देख सकता है, लेकिन जैसा कि मैंने कहा है, अर्थात् और शारीरिक रूप से यह असाइनमेंट नहीं है, यह एक विधि कॉल है। यह कहकर मेरा मतलब था कि यह एक कंपाइलर बग प्रतीत होता है। संकलक को पता होना चाहिए कि इस संदर्भ में, यह एक असाइनमेंट नहीं है। –

+0

इसके अलावा, अभिव्यक्ति के चारों ओर घुंघराले ब्रैकेट डालने का मतलब है कि यह अब एक लैम्ब्डा नहीं है, यह एक सादा प्रतिनिधि है, जिसका अर्थ है कि इसे अभिव्यक्ति वृक्ष में परिवर्तित नहीं किया जा सकता है। –

+0

मुझे पहले श्रोता निर्माण के बिंदु को पहली जगह नहीं मिला:/ – Timbo

1

आप अभिव्यक्ति वर्ग का उपयोग क्यों करना चाहते हैं की कोशिश की? Expression<Action<TProducer, TConsumer>> को अपने कोड में Action<TProducer, TConsumer> पर बदलें और सभी को वांछित काम करना चाहिए। आप यहां क्या कर रहे हैं संकलक को लैम्ब्डा अभिव्यक्ति को एक प्रतिनिधि के बजाय एक अभिव्यक्ति वृक्ष के रूप में इलाज करने के लिए मजबूर कर रहा है, और एक अभिव्यक्ति वृक्ष में वास्तव में ऐसे असाइनमेंट नहीं हो सकते हैं (इसे असाइनमेंट के रूप में माना जाता है क्योंकि आप + = ऑपरेटर का उपयोग कर रहे हैं)। अब, एक लैम्ब्डा अभिव्यक्ति को किसी भी रूप में परिवर्तित किया जा सकता है (जैसा कि [एमएसडीएन] [1] पर बताया गया है)। एक प्रतिनिधि का उपयोग करके (वह सभी एक्शन क्लास है), ऐसे "असाइनमेंट" पूरी तरह मान्य हैं।मैंने यहां समस्या को गलत समझा होगा (शायद एक विशिष्ट कारण है कि आपको अभिव्यक्ति वृक्ष का उपयोग करने की आवश्यकता क्यों है?), लेकिन ऐसा लगता है कि समाधान सौभाग्य से यह सरल है!

संपादित करें: ठीक है, मैं आपकी समस्या को अब टिप्पणी से थोड़ा बेहतर समझता हूं। क्या कोई कारण है कि आप वायरअप विधि के लिए तर्क के रूप में पी। MyEvent और c.MyHandler को पास नहीं कर सकते हैं और वायरअप विधि के भीतर ईवेंट हैंडलर संलग्न कर सकते हैं (मेरे लिए यह एक डिज़ाइन बिंदु दृश्य से भी बेहतर लगता है) ... होगा जो एक अभिव्यक्ति पेड़ की आवश्यकता को खत्म नहीं करता है? मुझे लगता है कि यदि आप अभिव्यक्ति के पेड़ से बचते हैं तो यह सबसे अच्छा है, क्योंकि वे प्रतिनिधियों की तुलना में धीमे होते हैं।

+0

क्योंकि मुझे अभिव्यक्ति के पेड़ तक पहुंचने की आवश्यकता है ताकि वह कौन सी घटना को पकड़ सके और कौन सा हैंडलर विधि वायर्ड हो। अभिव्यक्ति के पेड़ के बिना आप यह नहीं कर सकते हैं। –

+0

पर्याप्त मेला। मेरा पोस्ट अब एक वैकल्पिक समाधान प्रदान करने के लिए अद्यतन किया गया है। – Noldorin

+1

दुर्भाग्यवश, पी। माइवेन्ट को पकड़ने के लिए कोई दृढ़ता से टाइप नहीं किया गया प्रतीत होता है - बिना इसका उपयोग करने की कोशिश कर रहा है + = त्रुटि में त्रुटि CS0070: घटना 'निर्माता। MyEvent' केवल बाईं ओर दिखाई दे सकती है + = या - = (जब 'निर्माता' प्रकार के भीतर से उपयोग किया जाता है) –

0

मुझे लगता है कि समस्या यह है कि Expression<TDelegate> ऑब्जेक्ट के अलावा, अभिव्यक्ति वृक्ष संकलक के परिप्रेक्ष्य से स्थिर रूप से टाइप नहीं किया गया है। MethodCallExpression और दोस्तों स्थिर टाइपिंग जानकारी का पर्दाफाश नहीं करते हैं।

हालांकि संकलक अभिव्यक्ति में सभी प्रकारों को जानता है, भले ही लैम्ब्डा अभिव्यक्ति को अभिव्यक्ति वृक्ष में परिवर्तित करते समय यह जानकारी फेंक दी जाती है। (अभिव्यक्ति पेड़ के लिए उत्पन्न कोड पर एक नज़र डालें)

मैं फिर भी इसे माइक्रोसॉफ्ट को सबमिट करने पर विचार करता हूं।

1

असल में, जहां तक ​​संकलक उस बिंदु पर चिंतित है, एक असाइनमेंट है। + = ऑपरेटर ओवरलोड हो गया है, लेकिन कंपाइलर इसके बारे में उस पर ध्यान नहीं देता है। आखिरकार, आप लैम्ब्डा के माध्यम से एक अभिव्यक्ति उत्पन्न कर रहे हैं (जो, एक बिंदु पर वास्तविक कोड में संकलित किया जाएगा) और कोई वास्तविक कोड नहीं है।

तो कंपाइलर क्या करता है कहता है: c.MyHandler को p.MyEvent के वर्तमान मान में जोड़ने के लिए एक अभिव्यक्ति बनाएं और बदले गए मान को p.MyEvent में संग्रहीत करें। और इसलिए आप वास्तव में एक असाइनमेंट कर रहे हैं, भले ही अंत में आप नहीं हैं।

क्या कोई कारण है कि आप वायरअप विधि को अभिव्यक्ति करने के लिए चाहते हैं, न सिर्फ एक क्रिया?

+0

क्योंकि मुझे अभिव्यक्ति के पेड़ तक पहुंचने की आवश्यकता है ताकि कौन सी घटना को पकड़ने में सक्षम हो और कौन सा हैंडलर विधि वायर्ड हो। अभिव्यक्ति के पेड़ के बिना आप यह नहीं कर सकते हैं। –

+0

लेकिन, मुझे लगता है कि आपका क्या मतलब है, मुझे लगता है कि इस उत्तर का मुख्य हिस्सा "उस बिंदु पर" है ... मुझे प्रश्न संपादित करने दें। –

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