एक अज्ञात विधि है? क्या यह वास्तव में अनाम है? इसका कोई नाम है? सभी अच्छे प्रश्न, तो चलिए उनके साथ शुरू करते हैं और साथ ही साथ लैम्ब्डा अभिव्यक्तियों तक पहुंचते हैं।
public void TestSomething()
{
Test(delegate { Debug.WriteLine("Test"); });
}
क्या वास्तव में होता है:
आप इस कब मिलेंगे?
Debug.WriteLine("Test");
और कहा कि अलग एक विधि में:
संकलक पहली विधि है, जो यह है की "शरीर" लेने का फैसला।
- कहाँ मैं विधि रखना चाहिए:
दो सवाल संकलक अब जवाब देने के लिए है?
- विधि का हस्ताक्षर कैसा दिखना चाहिए?
दूसरा प्रश्न आसानी से उत्तर दिया गया है। delegate {
भाग इसका उत्तर देता है। विधि कोई पैरामीटर (delegate
और {
के बीच कुछ भी नहीं है) लेता है, और के बाद से हम इसके नाम (इसलिए "गुमनाम" भाग) के बारे में परवाह नहीं है, हम इस तरह के रूप में प्रणाली की घोषणा कर सकते हैं:
public void SomeOddMethod()
{
Debug.WriteLine("Test");
}
लेकिन क्यों किया यह सब कुछ करो?
चलिए देखते हैं कि Action
वास्तव में क्या प्रतिनिधि है।
एक प्रतिनिधि यह है कि, यदि हम एक पल के लिए इस तथ्य की उपेक्षा करते हैं कि इसमें प्रतिनिधि हैं।नेट वास्तव में दो बातें करने के लिए कई की सूची से जुड़े हुए हैं एकल "प्रतिनिधियों", एक संदर्भ (सूचक):
- एक वस्तु उदाहरण
- कि वस्तु उदाहरण पर एक विधि
तो, के साथ कि ज्ञान, कोड की पहली टुकड़ा वास्तव में इस के रूप में लिखा जा सकता है:
public void TestSomething()
{
Test(new Action(this.SomeOddMethod));
}
private void SomeOddMethod()
{
Debug.WriteLine("Test");
}
अब, इस के साथ समस्या यह है कि कंप्यूटर अनुप्रयोग इलर को यह जानने का कोई तरीका नहीं है कि Test
वास्तव में प्रतिनिधि के साथ क्या करता है, और प्रतिनिधि के एक आधे उदाहरण के संदर्भ में है, जिस पर विधि को कॉल किया जाना है, this
उपर्युक्त उदाहरण में, हम नहीं जानते कितना डेटा संदर्भित किया जाएगा।
उदाहरण के लिए, मान लें कि उपरोक्त कोड वास्तव में एक विशाल वस्तु का हिस्सा था, लेकिन एक वस्तु जो केवल अस्थायी रूप से रहती है। यह भी मान लें कि Test
उस प्रतिनिधि को कहीं भी स्टोर करेगा, यह लंबे समय तक जी रहेगा। वह "लंबा समय" उस विशाल वस्तु के जीवन को अपने आप में बांध देगा, इसके लिए एक लंबे समय तक संदर्भ भी रखेगा, शायद अच्छा नहीं।
तो संकलक सिर्फ एक विधि बनाने से अधिक करता है, यह इसे पकड़ने के लिए एक वर्ग भी बनाता है। यह पहला सवाल है, मुझे इसे कहां रखना चाहिए?।
कोड ऊपर इस प्रकार इस प्रकार के रूप में लिखा जा सकता है:
public void TestSomething()
{
var temp = new SomeClass;
Test(new Action(temp.SomeOddMethod));
}
private class SomeClass
{
private void SomeOddMethod()
{
Debug.WriteLine("Test");
}
}
यही है, इस उदाहरण के लिए, क्या एक गुमनाम विधि वास्तव में सभी के बारे में है।
हालात में थोड़ा और अधिक बालों मिल अगर आप स्थानीय चर का उपयोग शुरू, इस उदाहरण पर विचार करें:
public void Test()
{
int x = 10;
Test(delegate { Debug.WriteLine("x=" + x); });
}
यह वही है हुड के नीचे होता है, या कम से कम इसके बहुत करीब कुछ:
public void TestSomething()
{
var temp = new SomeClass;
temp.x = 10;
Test(new Action(temp.SomeOddMethod));
}
private class SomeClass
{
public int x;
private void SomeOddMethod()
{
Debug.WriteLine("x=" + x);
}
}
कंपाइलर एक वर्ग बनाता है, उस वर्ग में विधि के सभी चर को लिफ्ट करता है, और अज्ञात प्रकार के फ़ील्ड तक पहुंच के लिए स्थानीय चरों तक पहुंच को फिर से लिखता है।
क्लास का नाम है, और विधि, थोड़ा अजीब है, के LINQPad पूछना यह क्या होगा करते हैं:
void Main()
{
int x = 10;
Test(delegate { Debug.WriteLine("x=" + x); });
}
public void Test(Action action)
{
action();
}
अगर मैं उत्पादन के लिए LINQPad पूछना इस कार्यक्रम का आईएल (मध्यवर्ती भाषा),
// var temp = new UserQuery+<>c__DisplayClass1();
IL_0000: newobj UserQuery+<>c__DisplayClass1..ctor
IL_0005: stloc.0 // CS$<>8__locals2
IL_0006: ldloc.0 // CS$<>8__locals2
// temp.x = 10;
IL_0007: ldc.i4.s 0A
IL_0009: stfld UserQuery+<>c__DisplayClass1.x
// var action = new Action(temp.<Main>b__0);
IL_000E: ldarg.0
IL_000F: ldloc.0 // CS$<>8__locals2
IL_0010: ldftn UserQuery+<>c__DisplayClass1.<Main>b__0
IL_0016: newobj System.Action..ctor
// Test(action);
IL_001B: call UserQuery.Test
Test:
IL_0000: ldarg.1
IL_0001: callvirt System.Action.Invoke
IL_0006: ret
<>c__DisplayClass1.<Main>b__0:
IL_0000: ldstr "x="
IL_0005: ldarg.0
IL_0006: ldfld UserQuery+<>c__DisplayClass1.x
IL_000B: box System.Int32
IL_0010: call System.String.Concat
IL_0015: call System.Diagnostics.Debug.WriteLine
IL_001A: ret
<>c__DisplayClass1..ctor:
IL_0000: ldarg.0
IL_0001: call System.Object..ctor
IL_0006: ret
यहाँ आप देख सकते हैं कि क्लास का नाम UserQuery+<>c__DisplayClass1
है, और विधि का नाम <Main>b__0
है: मैं इस मिलता है। मैंने सी # कोड में संपादित किया जिसने इस कोड का निर्माण किया, LINQPad उपरोक्त उदाहरण में आईएल के अलावा कुछ भी नहीं उत्पन्न करता है।
यह सुनिश्चित करने के लिए संकेतों से कम और अधिक से अधिक संकेत हैं कि आप दुर्घटना से नहीं कर सकते हैं एक प्रकार और/या विधि जो आपके लिए उत्पादित कंपाइलर से मेल खाता है।
तो मूल रूप से यह एक अज्ञात विधि क्या है।
तो यह क्या है?
Test(() => Debug.WriteLine("Test"));
वैसे, इस मामले में यह एक ही है, यह एक गुमनाम विधि के उत्पादन के लिए एक शॉर्टकट है।
आप दो तरह से यह लिख सकते हैं:
() => { ... code here ... }
() => ... single expression here ...
अपनी पहली रूप में आप सभी कोड आप एक सामान्य विधि शरीर में करना होगा लिख सकते हैं। अपने दूसरे रूप में आपको एक अभिव्यक्ति या कथन लिखने की अनुमति है। इस रूप में
() => ...
उसी तरह:
हालांकि, इस मामले में संकलक इस व्यवहार करेगा
delegate { ... }
वे अभी भी गुमनाम तरीकों रहे हैं, यह सिर्फ इतना है कि () =>
वाक्य रचना इसे पाने के लिए एक शॉर्टकट है।
तो यदि यह प्राप्त करने का शॉर्टकट है, तो हमारे पास यह क्यों है?
ठीक है, यह उस उद्देश्य के लिए जीवन को थोड़ा आसान बनाता है, जिसे LINQ है।
var customers = from customer in db.Customers
where customer.Name == "ACME"
select customer.Address;
इस कोड के रूप में लिखा है इस प्रकार है::
var customers =
db.Customers
.Where(customer => customer.Name == "ACME")
.Select(customer => customer.Address");
आप delegate { ... }
सिंटैक्स का उपयोग करना हो तो आप return ...
और इतने के साथ भाव पुनर्लेखन करना होगा
इस LINQ बयान पर विचार करें पर, और वे अधिक मज़ाकिया लगेंगे। इस प्रकार लैम्ब्डा सिंटैक्स को उपरोक्त जैसे कोड लिखते समय प्रोग्रामर के लिए जीवन आसान बनाने के लिए जोड़ा गया था।
तो अभिव्यक्ति क्या हैं?
अब तक मैं नहीं दिखाया Test
कैसे परिभाषित किया गया है, लेकिन इसके बाद के संस्करण कोड के लिए Test
को परिभाषित करते हैं:
public void Test(Action action)
यह पर्याप्त होना चाहिए। यह कहता है कि "मुझे एक प्रतिनिधि की आवश्यकता है, यह प्रकार की कार्रवाई है (कोई पैरामीटर नहीं ले रहा है, कोई मूल्य नहीं लौटा रहा है)"।
हालांकि, माइक्रोसॉफ्ट भी इस विधि को परिभाषित करने के लिए एक अलग तरीके कहा:
public void Test(Expression<Func<....>> expr)
ध्यान दें कि मैं एक हिस्सा वहाँ गिरा दिया, ....
हिस्सा है, की पीठ कि को मिलता है।
इस कोड, इस कॉल के साथ रखा:
Test(() => x + 10);
वास्तव में एक प्रतिनिधि में पारित नहीं होगा, न ही कुछ भी है कि (तुरंत) कहा जा सकता है।इसके बजाय, संकलक कुछ के लिए इस कोड को फिर से लिखने होगा समान (लेकिन नहीं सब पर) की तरह नीचे कोड:
var operand1 = new VariableReferenceOperand("x");
var operand2 = new ConstantOperand(10);
var expression = new AdditionOperator(operand1, operand2);
Test(expression);
मूल रूप से संकलक एक Expression<Func<...>>
वस्तु का निर्माण होगा, चर के संदर्भों वाले, शाब्दिक मान , ऑपरेटरों का इस्तेमाल इत्यादि। और उस ऑब्जेक्ट पेड़ को विधि में पास करें।
क्यों?
ठीक है, ऊपर db.Customers.Where(...)
भाग पर विचार करें।
क्या यह अच्छा नहीं होगा कि, सभी ग्राहकों (और उनके सभी डेटा) को डेटाबेस से क्लाइंट में डाउनलोड करने के बजाय, उन सभी के माध्यम से लूपिंग करना, यह पता लगाना कि किस ग्राहक का सही नाम है, आदि कोड वास्तव में होगा डेटाबेस को एक ही, सही, ग्राहक को एक बार खोजने के लिए कहें?
अभिव्यक्ति के पीछे यह उद्देश्य है। एंटीटी फ्रेमवर्क, लिंक 2 एसक्यूएल, या किसी अन्य ऐसे LINQ- सहायक डेटाबेस परत, उस अभिव्यक्ति को ले लेंगे, इसका विश्लेषण करें, इसे अलग करें, और डेटाबेस के विरुद्ध निष्पादित करने के लिए एक उचित प्रारूपित एसक्यूएल लिखें।
यह कभी कर सकता है अगर हम अभी भी आईएल युक्त विधियों को प्रतिनिधि दे रहे हैं। यह केवल बातें की एक जोड़ी की वजह से ऐसा कर सकते हैं: (आदि कोई बयान,)
- वाक्य रचना एक
Expression<Func<...>>
के लिए उपयुक्त एक लैम्ब्डा अभिव्यक्ति में अनुमति दी सीमित है
- लैम्ब्डा वाक्यविन्यास कर्ली कोष्ठक के बिना, जो संकलक इस कोड का एक सरल रूप है कि बताता है
तो चलिए को संक्षेप में दोहराएँ:
- बेनामी तरीकों वास्तव में नहीं कि सभी अनाम, वे एक नामित प्रकार के रूप में, अंत कर रहे हैं एक नामित विधि के साथ, केवल आप उन चीजों को नाम देने की जरूरत नहीं है अपने आप को
- यह हुड कि चीजें ले जाता है के तहत संकलक जादू का एक बहुत है चारों ओर आप के लिए
- भाव और प्रतिनिधि एक ही बातें
- भाव चौखटे पता करने के लिए क्या कोड करता है और कैसे चाहता है के लिए हैं में से कुछ को देखने के लिए दो तरीके हैं की जरूरत नहीं है, इसलिए है कि, ताकि वे प्रक्रिया को अनुकूलित करने के लिए उस ज्ञान का उपयोग कर सकते हैं (जैसे SQL कथन लिखना)
- प्रतिनिधियों के लिए हैं चौखटे हैं कि केवल विधि
फुटनोट कॉल करने के लिए सक्षम किया जा रहा बारे में चिंतित:
इस तरह के एक सरल अभिव्यक्ति के लिए ....
हिस्सा वापसी मान के प्रकार आप प्राप्त करने के लिए है अभिव्यक्ति से। () => ... simple expression ...
केवल अभिव्यक्ति देता है, जो कि कुछ मान देता है, और यह कई कथन नहीं हो सकता है।इस प्रकार, एक वैध अभिव्यक्ति प्रकार यह है: Expression<Func<int>>
, मूल रूप से, अभिव्यक्ति एक फ़ंक्शन (विधि) एक पूर्णांक मान लौटा रही है।
ध्यान दें कि "अभिव्यक्ति जो मूल्य लौटाती है" Expression<...>
पैरामीटर या प्रकारों की सीमा है, लेकिन प्रतिनिधि नहीं। यह पूरी तरह से कानूनी कोड है अगर Test
के पैरामीटर प्रकार एक Action
है:
Test(() => Debug.WriteLine("Test"));
जाहिर है, Debug.WriteLine("Test")
कुछ भी वापस नहीं करता है, लेकिन यह कानूनी है। यदि विधि Test
को अभिव्यक्ति की आवश्यकता है, तो ऐसा नहीं होगा, क्योंकि अभिव्यक्ति को एक मान वापस करना होगा।
क्या आपने इसे देखा है? और संभवतः अन्य प्रश्न? http://stackoverflow.com/questions/6008097/what-are-anonymous-methods-in-c –
मैं पूरी तरह से समझता हूं कि एक अनाम विधि/लैम्ब्डा अभिव्यक्ति क्या है और वहां उपयोग है, मेरा सवाल यह है कि क्या वे अज्ञात से किसी भी माध्यम से भिन्न होते हैं प्रतिनिधि या वे वही हैं – sarepta
ठीक है, मैंने अभी एक **** लोड टेक्स्ट लिखा है, इसलिए उम्मीद है कि आपको अपना जवाब मिल जाएगा, अगर मुझे नहीं पता :) –