कुछ C # lambda अभिव्यक्तियाँ स्टैटिक विधियों को क्यों संकलित करती हैं?


122

जैसा कि आप नीचे दिए गए कोड में देख सकते हैं, मैंने एक Action<>वस्तु को एक चर के रूप में घोषित किया है ।

क्या कोई कृपया मुझे बताएगा कि यह क्रिया विधि प्रतिनिधि स्थिर विधि की तरह क्यों व्यवहार करता है?

यह trueनिम्न कोड में क्यों लौटता है?

कोड:

public static void Main(string[] args)
{
    Action<string> actionMethod = s => { Console.WriteLine("My Name is " + s); };

    Console.WriteLine(actionMethod.Method.IsStatic);

    Console.Read();
}

आउटपुट:

उदाहरण के नमूने का उत्पादन

जवाबों:


153

यह सबसे अधिक संभावना है क्योंकि कोई क्लोजर नहीं हैं, उदाहरण के लिए:

int age = 25;
Action<string> withClosure = s => Console.WriteLine("My name is {0} and I am {1} years old", s, age);
Action<string> withoutClosure = s => Console.WriteLine("My name is {0}", s);
Console.WriteLine(withClosure.Method.IsStatic);
Console.WriteLine(withoutClosure.Method.IsStatic);

हो जाएगा ताकि उत्पादन falseके लिए withClosureऔर trueके लिएwithoutClosure

जब आप लैम्बडा एक्सप्रेशन का उपयोग करते हैं, तो कंपाइलर आपकी विधि को शामिल करने के लिए एक छोटा वर्ग बनाता है, यह कुछ इस तरह से संकलन करेगा (वास्तविक कार्यान्वयन सबसे अधिक संभावना है कि थोड़ा भिन्न होता है):

private class <Main>b__0
{
    public int age;
    public void withClosure(string s)
    {
        Console.WriteLine("My name is {0} and I am {1} years old", s, age)
    }
}

private static class <Main>b__1
{
    public static void withoutClosure(string s)
    {
        Console.WriteLine("My name is {0}", s)
    }
}

public static void Main()
{
    var b__0 = new <Main>b__0();
    b__0.age = 25;
    Action<string> withClosure = b__0.withClosure;
    Action<string> withoutClosure = <Main>b__1.withoutClosure;
    Console.WriteLine(withClosure.Method.IsStatic);
    Console.WriteLine(withoutClosure.Method.IsStatic);
}

आप परिणामी Action<string>उदाहरणों को वास्तव में इन उत्पन्न वर्गों के तरीकों की ओर देख सकते हैं ।


4
+1। पुष्टि कर सकते हैं - एक बंद किए बिना वे staticतरीकों के लिए एकदम सही उम्मीदवार हैं ।
साइमन व्हाइटहेड

3
मैं सिर्फ सुझाव देने जा रहा था कि इस प्रश्न को कुछ विस्तार की आवश्यकता थी, मैं लौट आया और यह वहाँ था। बहुत जानकारीपूर्ण - महान यह देखने के लिए कि संकलक कवर के तहत क्या कर रहा है।
Liath

4
@ लिट Ildasmवास्तव में क्या चल रहा है यह समझने के लिए उपयोगी है, मैं छोटे नमूनों की जांच करने ILके LINQPadलिए टैब का उपयोग करता हूं ।
लुकाज़ोइड

@Lukazoid क्या आप हमें बताएंगे कि आपको यह कंपाइलर आउटपुट कैसे मिला? ILDASM ऐसे आउटपुट नहीं देगा .. किसी भी उपकरण या सॉफ्टवेयर द्वारा?
नूनू

8
@nunu इस उदाहरण में, मैंने ILटैब का उपयोग किया LINQPadऔर C # का अनुमान लगाया। संकलित आउटपुट का वास्तविक C # समतुल्य प्राप्त करने के लिए कुछ विकल्प का उपयोग ILSpyया Reflectorसंकलित असेंबली में किया जाएगा, आपको सबसे अधिक संभावना है कि कुछ विकल्पों को निष्क्रिय करना होगा जो लैम्बडा प्रदर्शित करने का प्रयास करेंगे न कि संकलित उत्पन्न वर्ग।
लुकाज़ोइड

20

"एक्शन विधि" केवल कार्यान्वयन के साइड इफेक्ट के रूप में स्थिर है। यह एक अनाम विधि का मामला है जिसमें कोई भी पकड़ा गया चर नहीं है। चूंकि कोई कैप्चर किए गए वैरिएबल नहीं हैं, इसलिए इस विधि में सामान्य रूप से स्थानीय वैरिएबल के अतिरिक्त जीवन भर की आवश्यकता नहीं है। यदि यह अन्य स्थानीय चर का संदर्भ देता है, तो इसका जीवनकाल उन अन्य चर के जीवनकाल तक फैला हुआ है (सेकंड देखें। L.1.7, स्थानीय चर और सेकंड। N.15.5.1, C # 5.0 विनिर्देशन में कैप्चर किए गए बाहरी चर )।

ध्यान दें कि C # विनिर्देश केवल अनाम विधियों के बारे में "अभिव्यक्ति पेड़" में परिवर्तित होने की बात करता है, न कि "अनाम कक्षाएं"। जबकि अभिव्यक्ति ट्री को अतिरिक्त C # वर्गों के रूप में दर्शाया जा सकता है, उदाहरण के लिए, Microsoft कंपाइलर में, इस कार्यान्वयन की आवश्यकता नहीं है (जैसा कि सेकंड द्वारा स्वीकार किया गया है। C # 5.0 विनिर्देशन में M.5.3)। इसलिए, यह अपरिभाषित है कि अनाम फ़ंक्शन स्थिर है या नहीं। इसके अलावा, खंड K.6 अभिव्यक्ति पेड़ों के विवरण के रूप में बहुत खुला छोड़ देता है।


2
+1 किए गए कारणों के लिए इस व्यवहार को सबसे अधिक संभावना पर भरोसा नहीं किया जाना चाहिए; यह बहुत कार्यान्वयन कार्यान्वयन है।
लुकाज़ोइड

18

रोसेलिन में डेलिगेट कैशिंग व्यवहार को बदल दिया गया था। पहले, जैसा कि कहा गया है, कोई भी लंबोदर अभिव्यक्ति, जो चर पर कब्जा नहीं करती थी, को एक में संकलित किया गया थाstatic उसे कॉल साइट पर विधि में । रोजलिन ने इस व्यवहार को बदल दिया। अब, कोई भी लंबोदर, जो चर को पकड़ता है या नहीं, एक प्रदर्शन वर्ग में बदल जाता है:

इस उदाहरण को देखते हुए:

public class C
{
    public void M()
    {
        var x = 5;
        Action<int> action = y => Console.WriteLine(y);
    }
}

मूल संकलक आउटपुट:

public class C
{
    [CompilerGenerated]
    private static Action<int> CS$<>9__CachedAnonymousMethodDelegate1;
    public void M()
    {
        if (C.CS$<>9__CachedAnonymousMethodDelegate1 == null)
        {
            C.CS$<>9__CachedAnonymousMethodDelegate1 = new Action<int>(C.<M>b__0);
        }
        Action<int> arg_1D_0 = C.CS$<>9__CachedAnonymousMethodDelegate1;
    }
    [CompilerGenerated]
    private static void <M>b__0(int y)
    {
        Console.WriteLine(y);
    }
}

रोसलिन:

public class C
{
    [CompilerGenerated]
    private sealed class <>c__DisplayClass0
    {
        public static readonly C.<>c__DisplayClass0 CS$<>9__inst;
        public static Action<int> CS$<>9__CachedAnonymousMethodDelegate2;
        static <>c__DisplayClass0()
        {
            // Note: this type is marked as 'beforefieldinit'.
            C.<>c__DisplayClass0.CS$<>9__inst = new C.<>c__DisplayClass0();
        }
        internal void <M>b__1(int y)
        {
            Console.WriteLine(y);
        }
    }
    public void M()
    {
        Action<int> arg_22_0;
        if (arg_22_0 = C.
                       <>c__DisplayClass0.CS$<>9__CachedAnonymousMethodDelegate2 == null)
        {
            C.<>c__DisplayClass0.CS$<>9__CachedAnonymousMethodDelegate2 =
          new Action<int>(C.<>c__DisplayClass0.CS$<>9__inst.<M>b__1);
        }
    }
}

रोसेलिन में प्रतिनिधि व्यवहार परिवर्तन के बारे में बताता है कि यह परिवर्तन क्यों किया गया था।


2
धन्यवाद, मैं सोच रहा था कि मेरा फंक <int> f = () => 5 का तरीका स्थिर नहीं था
vc 74

2

C # 6 के रूप में, यह हमेशा इंस्टेंस विधियों के लिए डिफ़ॉल्ट होगा, और कभी भी स्थिर नहीं होगा (इसलिए actionMethod.Method.IsStatic हमेशा गलत होगा)।

यहाँ देखें: C # 5 में एक स्टेटिक विधि से C # 6 में एक लैंथ को बिना किसी कैप्चर के क्यों बदला गया है?

और यहाँ: CSC और रोसलिन संकलक के स्थिर लैम्ब्डा अभिव्यक्ति मूल्यांकन में अंतर?


1

विधि में कोई क्लोजर नहीं है और यह स्वयं एक स्टेटिक विधि (Console.WriteLine) को भी संदर्भित करता है, इसलिए मुझे उम्मीद है कि यह स्थिर होगा। विधि एक बंद करने के लिए एक संलग्न अनाम प्रकार की घोषणा करेगी, लेकिन इस उदाहरण में इसकी आवश्यकता नहीं है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.