List.ForEach(Console.WriteLine);
List.ForEach(s => Console.WriteLine(s));
मेरे लिए, अंतर विशुद्ध रूप से कॉस्मेटिक है, लेकिन क्या कोई सूक्ष्म कारण है कि एक को दूसरे पर क्यों पसंद किया जा सकता है?
List.ForEach(Console.WriteLine);
List.ForEach(s => Console.WriteLine(s));
मेरे लिए, अंतर विशुद्ध रूप से कॉस्मेटिक है, लेकिन क्या कोई सूक्ष्म कारण है कि एक को दूसरे पर क्यों पसंद किया जा सकता है?
जवाबों:
ILSpy के माध्यम से संकलित कोड को देखते हुए, वास्तव में दो संदर्भों में अंतर है। इस तरह एक सरलीकृत कार्यक्रम के लिए:
namespace ScratchLambda
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
internal class Program
{
private static void Main(string[] args)
{
var list = Enumerable.Range(1, 10).ToList();
ExplicitLambda(list);
ImplicitLambda(list);
}
private static void ImplicitLambda(List<int> list)
{
list.ForEach(Console.WriteLine);
}
private static void ExplicitLambda(List<int> list)
{
list.ForEach(s => Console.WriteLine(s));
}
}
}
ILSpy इसे विघटित करता है:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ScratchLambda
{
internal class Program
{
private static void Main(string[] args)
{
List<int> list = Enumerable.Range(1, 10).ToList<int>();
Program.ExplicitLambda(list);
Program.ImplicitLambda(list);
}
private static void ImplicitLambda(List<int> list)
{
list.ForEach(new Action<int>(Console.WriteLine));
}
private static void ExplicitLambda(List<int> list)
{
list.ForEach(delegate(int s)
{
Console.WriteLine(s);
}
);
}
}
}
यदि आप दोनों के लिए आईएल कॉल स्टैक को देखते हैं, तो स्पष्ट कार्यान्वयन में बहुत अधिक कॉल हैं (और एक उत्पन्न विधि बनाता है):
.method private hidebysig static
void ExplicitLambda (
class [mscorlib]System.Collections.Generic.List`1<int32> list
) cil managed
{
// Method begins at RVA 0x2093
// Code size 36 (0x24)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldsfld class [mscorlib]System.Action`1<int32> ScratchLambda.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
IL_0006: brtrue.s IL_0019
IL_0008: ldnull
IL_0009: ldftn void ScratchLambda.Program::'<ExplicitLambda>b__0'(int32)
IL_000f: newobj instance void class [mscorlib]System.Action`1<int32>::.ctor(object, native int)
IL_0014: stsfld class [mscorlib]System.Action`1<int32> ScratchLambda.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
IL_0019: ldsfld class [mscorlib]System.Action`1<int32> ScratchLambda.Program::'CS$<>9__CachedAnonymousMethodDelegate1'
IL_001e: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::ForEach(class [mscorlib]System.Action`1<!0>)
IL_0023: ret
} // end of method Program::ExplicitLambda
.method private hidebysig static
void '<ExplicitLambda>b__0' (
int32 s
) cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01 00 00 00
)
// Method begins at RVA 0x208b
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call void [mscorlib]System.Console::WriteLine(int32)
IL_0006: ret
} // end of method Program::'<ExplicitLambda>b__0'
जबकि लागू कार्यान्वयन अधिक संक्षिप्त है:
.method private hidebysig static
void ImplicitLambda (
class [mscorlib]System.Collections.Generic.List`1<int32> list
) cil managed
{
// Method begins at RVA 0x2077
// Code size 19 (0x13)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldnull
IL_0002: ldftn void [mscorlib]System.Console::WriteLine(int32)
IL_0008: newobj instance void class [mscorlib]System.Action`1<int32>::.ctor(object, native int)
IL_000d: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<int32>::ForEach(class [mscorlib]System.Action`1<!0>)
IL_0012: ret
} // end of method Program::ImplicitLambda
मैं सामान्य रूप से लंबोदर सिंटैक्स पसंद करूंगा । जब आप उसे देखते हैं, तो यह बताता है कि प्रकार क्या है। जब आप देखते हैं Console.WriteLine
, तो आपको आईडीई से पूछना होगा कि यह किस प्रकार का है। बेशक, इस तुच्छ उदाहरण में, यह स्पष्ट है, लेकिन सामान्य मामले में, यह इतना नहीं हो सकता है।
आपके द्वारा दिए गए दो उदाहरणों के साथ जब आप कहते हैं कि वे इसमें भिन्न हैं
List.ForEach(Console.WriteLine)
आप वास्तव में ForEach लूप को विधि लाइलाइन का उपयोग करने के लिए कह रहे हैं
List.ForEach(s => Console.WriteLine(s));
वास्तव में एक विधि को परिभाषित कर रहा है जिसे फ़ॉरच कॉल करेगा और फिर आप इसे बता रहे हैं कि वहां क्या संभालना है।
इसलिए साधारण एक लाइनर के लिए यदि आपकी विधि जिसे आप कॉल करने जा रहे हैं, उसी हस्ताक्षर को वहन करने की विधि है जिसे पहले से ही कहा जाता है मैं लैंबडा को परिभाषित नहीं करना पसंद करूंगा, मुझे लगता है कि यह थोड़ा अधिक पठनीय है।
असंगत लैम्ब्डा वाले तरीकों के लिए निश्चित रूप से जाने का एक अच्छा तरीका है, यह मानते हुए कि वे अत्यधिक जटिल नहीं हैं।
पहली पंक्ति को पसंद करने का एक बहुत मजबूत कारण है।
प्रत्येक प्रतिनिधि के पास एक Target
संपत्ति होती है, जो प्रतिनिधियों को उदाहरण के तरीकों का उल्लेख करने की अनुमति देती है, भले ही उदाहरण दायरे से बाहर हो गया हो।
public class A {
public int Data;
public void WriteData() {
Console.WriteLine(this.Data);
}
}
var a1 = new A() {Data=4};
Action action = a1.WriteData;
a1 = null;
हम कॉल नहीं कर सकते a1.WriteData();
क्योंकि a1
अशक्त है। हालांकि, हम action
एक समस्या के बिना प्रतिनिधि को आमंत्रित कर सकते हैं , और यह प्रिंट करेगा 4
, क्योंकि action
उस उदाहरण का संदर्भ है जिसके साथ विधि को बुलाया जाना चाहिए।
जब अनाम तरीकों को एक उदाहरण के संदर्भ में एक प्रतिनिधि के रूप में पारित किया जाता है, तो प्रतिनिधि अभी भी संबंधित वर्ग के लिए एक संदर्भ रखेगा, भले ही वह कुछ भी न हो:
public class Container {
private List<int> data = new List<int>() {1,2,3,4,5};
public void PrintItems() {
//There is an implicit reference to an instance of Container here
data.ForEach(s => Console.WriteLine(s));
}
}
इस विशिष्ट मामले में, यह मान लेना उचित है कि .ForEach
प्रतिनिधि को आंतरिक रूप से संग्रहीत नहीं किया जा रहा है, जिसका अर्थ होगा कि Container
और उसके सभी डेटा की आवृत्ति अभी भी बरकरार है। लेकिन उस की कोई ग्वारेंटी नहीं है; प्रतिनिधि को प्राप्त करने का तरीका प्रतिनिधि और उदाहरण पर अनिश्चित काल के लिए हो सकता है।
दूसरी ओर, स्थैतिक विधियों में संदर्भ के लिए कोई उदाहरण नहीं है। निम्नलिखित का उदाहरण के लिए एक निहित संदर्भ नहीं होगा Container
:
public class Container {
private List<int> data = new List<int>() {1,2,3,4,5};
public void PrintItems() {
//Since Console.WriteLine is a static method, there is no implicit reference
data.ForEach(Console.WriteLine);
}
}