2016-03-07 4 views
47

आंतरिक रूप से, कंपाइलर को विधियों के लिए लैम्ब्डा अभिव्यक्तियों का अनुवाद करना चाहिए। उस स्थिति में, क्या ये विधियां निजी या सार्वजनिक होंगी (या कुछ और) और क्या इसे बदलना संभव है?क्या सी # कंपाइलर लैम्ब्डा अभिव्यक्ति को सार्वजनिक या निजी विधि के रूप में मानता है?

+0

afaik संकलक इस लैम्ब्डा को विधि के रूप में एक पूरी कक्षा बनाता है। तो इसे कॉल करने में सक्षम होने के लिए, इसे कम से कम 'आंतरिक' होना चाहिए। लेकिन मैं कोई कंपाइलर विशेषज्ञ नहीं हूँ। –

+0

@ RenéVogt यह इस बात पर निर्भर करता है कि लैम्ब्डा कुछ कैप्चर करता है या नहीं। यदि ऐसा नहीं होता है, तो बंद करने की कक्षा की कोई आवश्यकता नहीं है। – hvd

+3

यदि यह सार्वजनिक था, तो आप इसे कैसे कॉल करेंगे? इसका नाम किसी भी व्यक्ति के लिए नहीं बल्कि कंपाइलर है। इसका उपयोग किसी भी व्यक्ति के साथ नहीं होता है, लेकिन इसमें निजी लेकिन कुछ भी होने का कोई कारण नहीं है। –

उत्तर

58

यह निर्भर करता है। विजुअल स्टूडियो के वर्तमान संस्करण के साथ, लैम्बडा को लागू करने वाली विधियां कभी भी सार्वजनिक नहीं होती हैं, लेकिन वे हमेशा निजी नहीं होती हैं। lambdas के कुछ संस्करणों का परीक्षण करने के लिए एक साधारण प्रोग्राम:

public class Program 
{ 
    public static void Main() 
    { 
     var program = new Program(); 
     Try("A", program.A); 
     Try("B", program.B); 
     Try("C", program.C); 
     Console.ReadKey(); 
    } 

    private static void Try(string name, Func<Action> generator) 
    { 
     var mi = generator().Method; 
     Console.WriteLine($"{name}: DeclaringType={mi.DeclaringType}, Attributes={mi.Attributes}"); 
    } 

    private Action A() =>() => { }; 
    private Action B() =>() => { ToString(); }; 
    private Action C() 
    { 
     var c = 1; 
     return() => c.ToString(); 
    } 
} 

प्रिंट

A: DeclaringType=Scratch.Program+<>c, Attributes=PrivateScope, Assembly, HideBySig 
B: DeclaringType=Scratch.Program, Attributes=PrivateScope, Private, HideBySig 
C: DeclaringType=Scratch.Program+<>c__DisplayClass4_0, Attributes=PrivateScope, Assembly, HideBySig 

A के लैम्ब्डा किसी भी कैप्चर नहीं है। यह एक खाली बंद कक्षा के internal विधि के रूप में बनाया गया है।

B का लैम्ब्डा this कैप्चर करता है। यह युक्त कक्षा के private विधि के रूप में बनाया गया है।

C का लैम्ब्डा c कैप्चर करता है। यह एक गैर-खाली बंद करने की कक्षा के internal विधि के रूप में बनाया गया है।

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

+0

अच्छा जवाब। लेकिन, कैप्चरिंग को विधि की दृश्यता के साथ क्या करना है? यदि 'ए' को 'निजी' पर सेट किया गया था (या 'बी' '' ''' पर सेट किया गया था तो गलत क्या हो सकता था)? – haim770

+5

@ haim770 अज्ञात विधि वाली विधि को प्रतिनिधि बनाने के लिए विधि तक पहुंचने में सक्षम होना आवश्यक है। यदि अज्ञात विधि एक ही कक्षा के सदस्य के रूप में बनाई गई है, तो यह 'निजी' हो सकती है, क्योंकि विधियां अपनी कक्षा के निजी तरीकों तक पहुंच सकती हैं। यदि अज्ञात विधि किसी भिन्न वर्ग के सदस्य के रूप में बनाई गई है, तो इसे कम से कम 'आंतरिक' होना चाहिए, क्योंकि विधियां अन्य कक्षाओं के निजी तरीकों तक नहीं पहुंच सकती हैं। बढ़ती दृश्यता (सब कुछ 'आंतरिक' बनाना) संभव होगा, लेकिन इसकी आवश्यकता से अधिक दृश्यमान बनाने का कोई कारण नहीं है। – hvd

+0

ध्यान दें कि उत्पन्न क्लोजर क्लास निजी है, इसलिए, बाहर से, विधि * प्रभावी रूप से निजी * है। – svick

7

जेफरी रिक्टर

संकलक स्वचालित रूप से वर्ग

में एक नया निजी विधि को परिभाषित करता है के द्वारा सी # पुस्तक के माध्यम से CLR से ... संकलक के लिए आप स्वचालित रूप से

विधि का नाम बनाता है

... कंपाइलर द्वारा उत्पन्न अज्ञात विधियां हमेशा को निजी होने पर समाप्त होती हैं, और विधि पर निर्भर करती है कि विधि किसी भी इंस्टेंस सदस्यों तक पहुंचती है या नहीं

तो विधि private या internal के रूप में घोषित की गई है।

उदाहरण कोड के लिए

class AClass { 
    public void SomeMethod() { 
     Action lambda =() => Console.WriteLine("Hello World"); 
     lambda(); 
    } 
} 

आप देख सकते हैं यह private static क्षेत्र है के रूप में

.field private static class [mscorlib]System.Action 'CS$<>9__CachedAnonymousMethodDelegate1' 

आईएल घोषणा का उत्पादन करेगा।

हालांकि लगता है कि लैम्ब्डा अभिव्यक्ति अनुकूलित किया जा सकता है, अगर आप को

class AClass 
{ 
    string a = "Hello World"; 

    public void SomeMethod() 
    { 
     Action lambda =() => Console.WriteLine(a); 
     lambda(); 
    } 
} 

संकलक उदाहरण बदलने यह अनुकूलित करेंगे और वहाँ सभी

IL_0001: ldstr  "Hello World" 
IL_0006: call  void [mscorlib]System.Console::WriteLine(string) 
+6

यह जानकारी अब VS2015 के रूप में सटीक नहीं है। एक अनुकूलन के रूप में, नया संकलक स्थैतिक तरीकों को बनाने से बचाता है, क्योंकि एक प्रतिनिधि के माध्यम से गैर-स्थैतिक विधियां एक छोटे प्रदर्शन में वृद्धि प्रदान करती हैं। – hvd

+4

@hvd मुझे हमेशा आश्चर्य होता है कि लोग इस –

+2

@AlexanderDerck जैसे सामानों को कैसे जानते हैं, डेवलपर्स द्वारा रोज़लिन मुद्दे ट्रैकर में क्या और क्यों उपलब्ध थे। – hvd

2

में कोई लैम्ब्डा घोषणा किया जाएगा के रूप में @hvd वहाँ उल्लेख किया लैम्ब्डा अभिव्यक्ति के बीच एक अंतर इसके आसपास के पर्यावरण (बंद करने के मामले) से पैरामीटर का उपयोग करता है या नहीं। देखें: Why do some C# lambda expressions compile to static methods?

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

आप उस जेनरेट क्लास (जो मूल रूप से एक प्रतिनिधि को लपेटता है) पास कर सकते हैं और यह हमेशा परिभाषित असेंबली में जेनरेट किए गए प्रतिनिधि को संदर्भित करेगा। यदि असेंबली का संदर्भ दिया जाता है तो आप इसे कहीं से भी बुला सकते हैं।

बस सत्यापित है कि किसी अन्य असेंबली कार्यों में परिभाषित एक क्रिया को पारित करने और निष्पादित करने के बावजूद Action.Method स्वयं को आंतरिक चिह्नित किया गया है।

// Main, first assembly 
namespace ConsoleApplication1 
{ 
    public class B : IB 
    { 
     Action _action; 
     public void AddAction(Action act) 
     { 
      _action = act; 
     } 

     public void Invoke() 
     { 
      Console.WriteLine(_action.Target); 
      Console.WriteLine("Is public: {0}", _action.Method.IsPublic); 
      _action(); 
     } 

    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      var a = new A(); 
      var b = new B(); 
      a.AddActionTo(b); 
      b.Invoke(); 

      Console.ReadKey(); 
     } 
    } 
} 

अन्य विधानसभा में:

namespace OtherAssembly 
{ 
    public interface IB 
    { 
     void AddAction(Action act); 
    } 

    public class A 
    { 
     public void AddActionTo(IB b) 
     { 
      Action act =() => { }; 
      b.AddAction(act); 
     } 
    } 
} 
25

आंतरिक रूप से, संकलक तरीकों के लैम्ब्डा भाव का अनुवाद होना चाहिए।

मुझे लगता है कि "लैम्ब्डा" का मतलब है कि आप एक लैम्ब्डा को एक प्रतिनिधि प्रकार में परिवर्तित कर देते हैं। अभिव्यक्ति वृक्ष प्रकारों में परिवर्तित लैम्ब्डा निश्चित रूप से विधियों के रूप में उत्पन्न नहीं होते हैं।

कंपाइलर वास्तव में ऐसे लैम्ब्स को विधियों में बदल देता है, हां। आवश्यकता है जो ऐसा करता है, लेकिन ऐसा करना सुविधाजनक है।

उस स्थिति में, क्या ये विधियां निजी या सार्वजनिक होंगी (या कुछ और) और क्या इसे बदलना संभव है?

प्रश्न कुछ हद तक असंगत है। मान लीजिए मैंने आपको बताया था कि एक लैम्ब्डा एक सार्वजनिक विधि थी। इसका नाम सी # से सुलभ नहीं है; आप अपने सार्वजनिक दास का लाभ कैसे लेंगे? अभिगम्यता संशोधक नाम वाले सदस्यों पर लागू होते हैं। अभिगम्यता डोमेन की बहुत धारणा नाम का डोमेन नाम समाधान के दौरान देता है।

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

फिर से, इनमें से कोई भी आवश्यक नहीं है, और यह सब परिवर्तन के अधीन कार्यान्वयन विवरण है। आपको कंपाइलर के कोड जनरेशन विवरण का लाभ उठाने का प्रयास नहीं करना चाहिए।

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